Skip to content

Commit 3cff05d

Browse files
committed
[embedded] Implement swift_dynamicCast suport for casts from existential to concrete type
1 parent a725de5 commit 3cff05d

File tree

9 files changed

+318
-15
lines changed

9 files changed

+318
-15
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ private struct FunctionChecker {
9090
is ExistentialMetatypeInst:
9191
if !context.options.enableEmbeddedSwiftExistentials {
9292
throw Diagnostic(.embedded_swift_existential_type, instruction.operands[0].value.type, at: instruction.location)
93+
} else if let ie = instruction as? InitExistentialAddrInst {
94+
for conf in ie.conformances {
95+
try checkConformance(conf, location: ie.location)
96+
}
9397
}
9498

9599
case let aeb as AllocExistentialBoxInst:
@@ -116,7 +120,20 @@ private struct FunctionChecker {
116120

117121
case is CheckedCastAddrBranchInst,
118122
is UnconditionalCheckedCastAddrInst:
119-
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
123+
if !context.options.enableEmbeddedSwiftExistentials {
124+
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
125+
} else {
126+
if let checkedCast = instruction as? CheckedCastAddrBranchInst {
127+
if !checkedCast.supportedInEmbeddedSwift {
128+
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
129+
}
130+
} else {
131+
let checkedCast = instruction as! UnconditionalCheckedCastAddrInst
132+
if !checkedCast.supportedInEmbeddedSwift {
133+
throw Diagnostic(.embedded_swift_dynamic_cast, at: instruction.location)
134+
}
135+
}
136+
}
120137

121138
case let abi as AllocBoxInst:
122139
// 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 {
221238
else {
222239
return
223240
}
224-
if !conformance.protocol.requiresClass {
241+
if !context.options.enableEmbeddedSwiftExistentials &&
242+
!conformance.protocol.requiresClass {
225243
throw Diagnostic(.embedded_swift_existential_protocol, conformance.protocol.name, at: location)
226244
}
227245

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,23 @@ func canDynamicallyCast(from sourceType: CanonicalType, to destType: CanonicalTy
10011001
}
10021002
}
10031003

1004+
func isCastSupportedInEmbeddedSwift(from sourceType: Type,
1005+
to destType: Type ) -> Bool {
1006+
if !sourceType.isExistential {
1007+
return false
1008+
}
1009+
if destType.hasArchetype {
1010+
return false
1011+
}
1012+
1013+
// Tuple?
1014+
if !destType.isStruct && !destType.isClass && !destType.isEnum {
1015+
return false
1016+
}
1017+
1018+
return true
1019+
}
1020+
10041021
extension CheckedCastAddrBranchInst {
10051022
var dynamicCastResult: Bool? {
10061023
switch classifyDynamicCastBridged(bridged) {
@@ -1010,6 +1027,18 @@ extension CheckedCastAddrBranchInst {
10101027
default: fatalError("unknown result from classifyDynamicCastBridged")
10111028
}
10121029
}
1030+
1031+
var supportedInEmbeddedSwift: Bool {
1032+
return isCastSupportedInEmbeddedSwift(from: source.type,
1033+
to: destination.type)
1034+
}
1035+
}
1036+
1037+
extension UnconditionalCheckedCastAddrInst {
1038+
var supportedInEmbeddedSwift: Bool {
1039+
return isCastSupportedInEmbeddedSwift(from: source.type,
1040+
to: destination.type)
1041+
}
10131042
}
10141043

10151044
extension CopyAddrInst {

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,11 @@ final public
879879
class OpenExistentialValueInst : SingleValueInstruction, UnaryInstruction {}
880880

881881
final public
882-
class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction {}
882+
class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction {
883+
public var conformances: ConformanceArray {
884+
ConformanceArray(bridged: bridged.InitExistentialAddrInst_getConformances())
885+
}
886+
}
883887

884888
final public
885889
class DeinitExistentialAddrInst : Instruction, UnaryInstruction {}

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ struct BridgedInstruction {
797797
BRIDGED_INLINE bool IndexAddrInst_needsStackProtection() const;
798798
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialRefInst_getConformances() const;
799799
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialRefInst_getFormalConcreteType() const;
800+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialAddrInst_getConformances() const;
800801
BRIDGED_INLINE bool OpenExistentialAddr_isImmutable() const;
801802
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar GlobalAccessInst_getGlobal() const;
802803
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar AllocGlobalInst_getGlobal() const;

include/swift/SIL/SILBridgingImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,9 @@ BridgedCanType BridgedInstruction::InitExistentialRefInst_getFormalConcreteType(
12491249
return getAs<swift::InitExistentialRefInst>()->getFormalConcreteType();
12501250
}
12511251

1252+
BridgedConformanceArray BridgedInstruction::InitExistentialAddrInst_getConformances() const {
1253+
return {getAs<swift::InitExistentialAddrInst>()->getConformances()};
1254+
}
12521255
bool BridgedInstruction::OpenExistentialAddr_isImmutable() const {
12531256
switch (getAs<swift::OpenExistentialAddrInst>()->getAccessKind()) {
12541257
case swift::OpenedExistentialAccess::Immutable: return true;

lib/IRGen/GenCast.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,14 @@ llvm::Value *irgen::emitCheckedCast(IRGenFunction &IGF,
8585
src = IGF.Builder.CreateElementBitCast(src, IGF.IGM.OpaqueTy);
8686

8787
// Load type metadata for the source's static type and the target type.
88-
llvm::Value *srcMetadata = IGF.emitTypeMetadataRef(srcType);
88+
llvm::Value *srcMetadata = nullptr;
89+
90+
// Embedded swift currently only supports existential -> concrete type casts.
91+
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
92+
srcMetadata = llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy);
93+
} else {
94+
srcMetadata = IGF.emitTypeMetadataRef(srcType);
95+
}
8996
llvm::Value *targetMetadata = IGF.emitTypeMetadataRef(targetType);
9097

9198
llvm::Value *args[] = {

stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ typedef struct {
9696
void* (*initializeWithCopyFn)(void*, void*, void*);
9797
#endif
9898
void *assignWithCopyFn;
99-
void *initializeWithTakeFn;
99+
#if __has_feature(ptrauth_calls)
100+
void* (* __ptrauth(0, 1, 0x48d8) initializeWithTakeFn)(void*, void*, void*);
101+
#else
102+
void* (*initializeWithTakeFn)(void *, void*, void*);
103+
#endif
100104
void *assignWithTakeFn;
101105
void *getEnumTagSinglePayloadFn;
102106
void *storeEnumTagSinglePayload;
@@ -113,43 +117,105 @@ typedef struct {
113117
#endif
114118
} EmbeddedMetaDataPrefix;
115119

120+
typedef enum {
121+
AlignmentMask = 0x000000FF,
122+
IsNonInline = 0x00020000,
123+
} ValueWitnessTableFlags;
124+
125+
static inline
126+
EmbeddedMetaDataPrefix *_swift_embedded_get_full_metadata(void *metadata) {
127+
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
128+
return fullmeta;
129+
}
130+
116131
static inline __swift_size_t
117132
_swift_embedded_metadata_get_size(void *metadata) {
118-
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
133+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
119134
return fullmeta->vwt->size;
120135
}
121136

122137
static inline __swift_size_t
123138
_swift_embedded_metadata_get_align_mask_impl(EmbeddedMetaDataPrefix *fullMetadata) {
124139
unsigned flags = fullMetadata->vwt->flags;
125-
unsigned embeddedValueWitnessTableFlagsMask = 0xFF;
126-
127-
return flags & embeddedValueWitnessTableFlagsMask;
140+
ValueWitnessTableFlags alignMask = AlignmentMask;
141+
return flags & alignMask;
128142
}
129143

130144
static inline __swift_size_t
131145
_swift_embedded_metadata_get_align_mask(void *metadata) {
132-
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
146+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
133147
return _swift_embedded_metadata_get_align_mask_impl(fullmeta);
134148
}
135149

136-
static inline void
137-
_swift_embedded_invoke_box_destroy(void *object) {
138-
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
139-
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
150+
static inline void *
151+
_swift_embedded_box_project(void *object, EmbeddedMetaDataPrefix *fullmeta) {
140152
__swift_size_t alignMask = _swift_embedded_metadata_get_align_mask_impl(fullmeta);
141153
__swift_size_t headerSize = sizeof(void*) + sizeof(__swift_size_t);
142154
__swift_size_t startOfBoxedValue = (headerSize + alignMask) & ~alignMask;
143155
void *addrInBox = (void *)(((unsigned char *)object) + startOfBoxedValue);
156+
return addrInBox;
157+
}
158+
static inline void
159+
_swift_embedded_invoke_box_destroy(void *object) {
160+
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
161+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
162+
void *addrInBox = _swift_embedded_box_project(object, fullmeta);
144163
fullmeta->vwt->destroyFn(addrInBox, metadata);
145164
}
146165

147166
static inline void
148167
_swift_embedded_initialize_box(void *metadata, void *newObjectAddr, void *oldObjectAddr) {
149-
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
168+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
150169
fullmeta->vwt->initializeWithCopyFn(newObjectAddr, oldObjectAddr, metadata);
151170
}
152171

172+
typedef struct {
173+
void *inlineBuffer[3];
174+
void *metadata;
175+
} ExistentialValue;
176+
177+
static inline void
178+
_swift_embedded_existential_destroy(void *exist) {
179+
ExistentialValue* existVal = (ExistentialValue*)exist;
180+
void *metadata = existVal->metadata;
181+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
182+
ValueWitnessTableFlags isNonInlineMask = IsNonInline;
183+
if (fullmeta->vwt->flags & IsNonInline) {
184+
void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta);
185+
fullmeta->vwt->destroyFn(addrInBox, metadata);
186+
} else {
187+
fullmeta->vwt->destroyFn(&(existVal->inlineBuffer[0]), metadata);
188+
}
189+
}
190+
191+
static inline void
192+
_swift_embedded_existential_init_with_take(void *dst, void *srcExist) {
193+
ExistentialValue* existVal = (ExistentialValue*)srcExist;
194+
void *metadata = existVal->metadata;
195+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
196+
ValueWitnessTableFlags isNonInlineMask = IsNonInline;
197+
if (fullmeta->vwt->flags & IsNonInline) {
198+
void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta);
199+
fullmeta->vwt->initializeWithTakeFn(dst, addrInBox, metadata);
200+
} else {
201+
fullmeta->vwt->initializeWithTakeFn(dst, &(existVal->inlineBuffer[0]), metadata);
202+
}
203+
}
204+
205+
static inline void
206+
_swift_embedded_existential_init_with_copy(void *dst, void *srcExist) {
207+
ExistentialValue* existVal = (ExistentialValue*)srcExist;
208+
void *metadata = existVal->metadata;
209+
EmbeddedMetaDataPrefix *fullmeta = _swift_embedded_get_full_metadata(metadata);
210+
ValueWitnessTableFlags isNonInlineMask = IsNonInline;
211+
if (fullmeta->vwt->flags & IsNonInline) {
212+
void *addrInBox = _swift_embedded_box_project(existVal->inlineBuffer[0], fullmeta);
213+
fullmeta->vwt->initializeWithCopyFn(dst, addrInBox, metadata);
214+
} else {
215+
fullmeta->vwt->initializeWithCopyFn(dst, &(existVal->inlineBuffer[0]), metadata);
216+
}
217+
}
218+
153219
#ifdef __cplusplus
154220
} // extern "C"
155221
#endif

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,113 @@ public func swifft_makeBoxUnique(buffer: Builtin.RawPointer, metadata: Builtin.R
315315
return (box, oldObjectAddr._rawValue)
316316
}
317317
}
318+
/// Dynamic cast support
319+
/// Only supports existential container to concrete casts.
320+
struct DynamicCastFlags {
321+
static let Default = UInt(bitPattern: 0x0)
322+
static let Unconditional = UInt(bitPattern: 0x1)
323+
static let TakeOnSuccess = UInt(bitPattern: 0x2)
324+
static let DestroyOnFailure = UInt(bitPattern: 0x4)
325+
}
326+
327+
struct MetadataKind {
328+
static let LastEnumerated = UInt(bitPattern: 0x7FF)
329+
}
330+
331+
func projectExistentialMetadata(
332+
_ exist: Builtin.RawPointer
333+
) -> Builtin.RawPointer {
334+
335+
let offset = (3 * MemoryLayout<Builtin.RawPointer>.size)
336+
let addrOfMetadata = unsafe UnsafeMutableRawPointer(exist) + offset
337+
let metadataPtrAddr = unsafe addrOfMetadata.bindMemory(to: Builtin.RawPointer.self, capacity: 1)
338+
return unsafe metadataPtrAddr.pointee
339+
}
340+
341+
func isClassMetadata(_ metadata: Builtin.RawPointer) -> Bool {
342+
let addrOfMetadataKind = unsafe UnsafePointer<UInt>(metadata)
343+
let kind = unsafe addrOfMetadataKind.pointee
344+
return kind == 0 || kind > MetadataKind.LastEnumerated
345+
}
346+
347+
func projectHeapObject(_ exist: Builtin.RawPointer) -> UnsafeMutableRawPointer{
348+
let addrOfHeapObject = unsafe UnsafePointer<UnsafeMutableRawPointer>(exist)
349+
return unsafe addrOfHeapObject.pointee
350+
}
351+
352+
@c
353+
public func swift_dynamicCast(
354+
_ dest: Builtin.RawPointer, /* points to a concrete type */
355+
_ src: Builtin.RawPointer, /* points to an existential */
356+
_ srcMetadata: Builtin.RawPointer, /* always nullptr */
357+
_ dstMetadata: Builtin.RawPointer,
358+
_ flags: UInt
359+
) -> Bool {
360+
361+
let isUnconditionalCast : Bool = (flags & DynamicCastFlags.Unconditional) != 0
362+
let isTakeOnSuccess : Bool = (flags & DynamicCastFlags.TakeOnSuccess) != 0
363+
let isDestroyOnFailure : Bool =
364+
(flags & DynamicCastFlags.DestroyOnFailure) != 0
365+
366+
let srcMetadata = projectExistentialMetadata(src)
367+
368+
let isClass = isClassMetadata(dstMetadata)
369+
370+
if isClass {
371+
var success = false
372+
let obj = unsafe projectHeapObject(src)
373+
if isClassMetadata(srcMetadata) {
374+
if srcMetadata != dstMetadata {
375+
// check parent chain
376+
success = unsafe swift_dynamicCastClass(object: obj, targetMetadata: UnsafeMutableRawPointer(dstMetadata)) != nil
377+
} else {
378+
success = true
379+
}
380+
}
381+
if !success {
382+
if isDestroyOnFailure {
383+
unsafe _swift_embedded_existential_destroy(UnsafeMutableRawPointer(src))
384+
}
385+
386+
if isUnconditionalCast {
387+
fatalError("failed cast")
388+
}
389+
return false
390+
}
391+
if isTakeOnSuccess {
392+
let dst = unsafe UnsafeMutablePointer<UnsafeMutableRawPointer>(dest)
393+
unsafe dst.pointee = obj
394+
395+
} else {
396+
let dst = unsafe UnsafeMutablePointer<UnsafeMutableRawPointer>(dest)
397+
unsafe dst.pointee = obj
398+
swift_retain(object: obj._rawValue)
399+
}
400+
return true;
401+
}
402+
// destintation type is not a class. Test exact match.
403+
let success = srcMetadata == dstMetadata
404+
if !success {
405+
if isDestroyOnFailure {
406+
unsafe _swift_embedded_existential_destroy(UnsafeMutableRawPointer(src))
407+
}
408+
409+
if isUnconditionalCast {
410+
fatalError("failed cast")
411+
}
412+
return false
413+
}
414+
if isTakeOnSuccess {
415+
// take from an existential to a concrete type
416+
unsafe _swift_embedded_existential_init_with_take(
417+
UnsafeMutableRawPointer(dest), UnsafeMutableRawPointer(src))
418+
} else {
419+
// copy from an existential to a concrete type
420+
unsafe _swift_embedded_existential_init_with_copy(
421+
UnsafeMutableRawPointer(dest), UnsafeMutableRawPointer(src))
422+
}
423+
return true
424+
}
318425

319426
/// Refcounting
320427

0 commit comments

Comments
 (0)