From e157f809e86f9496fb497b46a0cfb27b0bb6dac7 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Tue, 20 Jun 2023 17:24:53 -0700 Subject: [PATCH] [IRGen+Runtime] Add layout string support for generic single payload enums --- include/swift/Runtime/Enum.h | 5 + include/swift/Runtime/RuntimeFunctions.def | 12 ++ lib/IRGen/GenEnum.cpp | 21 ++- lib/IRGen/GenMeta.cpp | 8 +- lib/IRGen/GenValueWitness.cpp | 8 +- lib/IRGen/TypeLayout.cpp | 7 +- stdlib/public/runtime/BytecodeLayouts.cpp | 74 +++++++- stdlib/public/runtime/BytecodeLayouts.h | 7 +- stdlib/public/runtime/Enum.cpp | 176 +++++++++++++++++- stdlib/public/runtime/Metadata.cpp | 7 +- .../layout_string_witnesses_types.swift | 2 +- .../layout_string_witnesses_dynamic.swift | 28 +++ 12 files changed, 327 insertions(+), 28 deletions(-) diff --git a/include/swift/Runtime/Enum.h b/include/swift/Runtime/Enum.h index 4eaa6c8ac2032..0081e447be5ab 100644 --- a/include/swift/Runtime/Enum.h +++ b/include/swift/Runtime/Enum.h @@ -63,6 +63,11 @@ void swift_initEnumMetadataSinglePayload(EnumMetadata *enumType, const TypeLayout *payload, unsigned emptyCases); +SWIFT_RUNTIME_EXPORT +void swift_initEnumMetadataSinglePayloadWithLayoutString( + EnumMetadata *enumType, EnumLayoutFlags flags, const Metadata *payload, + unsigned emptyCases); + using getExtraInhabitantTag_t = SWIFT_CC(swift) unsigned (const OpaqueValue *value, unsigned numExtraInhabitants, diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 0d88cd1258647..518ee17fb1f05 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -1323,6 +1323,18 @@ FUNCTION(InitEnumMetadataSinglePayload, ATTRS(NoUnwind, WillReturn), EFFECT(MetaData)) +// void swift_initEnumMetadataSinglePayloadWithLayoutString(Metadata *enumType, +// EnumLayoutFlags flags, +// Metadata *payload, +// unsigned num_empty_cases); +FUNCTION(InitEnumMetadataSinglePayloadWithLayoutString, + swift_initEnumMetadataSinglePayloadWithLayoutString, + C_CC, AlwaysAvailable, + RETURNS(VoidTy), + ARGS(TypeMetadataPtrTy, SizeTy, TypeMetadataPtrTy, Int32Ty), + ATTRS(NoUnwind, WillReturn), + EFFECT(MetaData)) + // void swift_initEnumMetadataMultiPayload(Metadata *enumType, // EnumLayoutFlags layoutFlags, // size_t numPayloads, diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index 18f1f7f28f4ad..dee1246ff559a 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -3237,8 +3237,25 @@ namespace { void initializeMetadataWithLayoutString( IRGenFunction &IGF, llvm::Value *metadata, bool isVWTMutable, SILType T, MetadataDependencyCollector *collector) const override { - // Not yet supported on this type, so forward to regular method - initializeMetadata(IGF, metadata, isVWTMutable, T, collector); + // Fixed-size enums don't need dynamic witness table initialization. + if (TIK >= Fixed) + return; + + // Ask the runtime to do our layout using the payload metadata and number + // of empty cases. + auto payloadTy = + T.getEnumElementType(ElementsWithPayload[0].decl, IGM.getSILModule(), + IGM.getMaximalTypeExpansionContext()); + + auto request = DynamicMetadataRequest::getNonBlocking( + MetadataState::LayoutComplete, collector); + auto payloadLayout = IGF.emitTypeMetadataRefForLayout(payloadTy, request); + auto emptyCasesVal = + llvm::ConstantInt::get(IGM.Int32Ty, ElementsWithNoPayload.size()); + auto flags = emitEnumLayoutFlags(IGM, isVWTMutable); + IGF.Builder.CreateCall( + IGM.getInitEnumMetadataSinglePayloadWithLayoutStringFunctionPointer(), + {metadata, flags, payloadLayout, emptyCasesVal}); } /// \group Extra inhabitants diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 3a67800eb009e..5cebb564fc559 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5736,18 +5736,12 @@ namespace { return false; } - auto &strategy = getEnumImplStrategy(IGM, getLoweredType()); - bool isSupportedCase = strategy.getElementsWithPayload().size() > 1 || - (strategy.getElementsWithPayload().size() == 1 && - strategy.getElementsWithNoPayload().empty()); - return !!getLayoutString() || (IGM.Context.LangOpts.hasFeature( Feature::LayoutStringValueWitnessesInstantiation) && IGM.getOptions().EnableLayoutStringValueWitnessesInstantiation && (HasDependentVWT || HasDependentMetadata) && - !isa(IGM.getTypeInfo(getLoweredType())) && - isSupportedCase); + !isa(IGM.getTypeInfo(getLoweredType()))); } llvm::Constant *emitNominalTypeDescriptor() { diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index fb9693e6857d2..b43fed1df5626 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -891,11 +891,9 @@ bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM, IGM.Context.LangOpts.hasFeature( Feature::LayoutStringValueWitnessesInstantiation) && IGM.getOptions().EnableLayoutStringValueWitnessesInstantiation) { - if (auto *enumEntry = typeLayoutEntry->getAsEnum()) { - return enumEntry->isMultiPayloadEnum() || enumEntry->isSingleton(); - } - return (typeLayoutEntry->isAlignedGroup() && - !typeLayoutEntry->isFixedSize(IGM)); + return ( + (typeLayoutEntry->isAlignedGroup() || typeLayoutEntry->getAsEnum()) && + !typeLayoutEntry->isFixedSize(IGM)); } return false; diff --git a/lib/IRGen/TypeLayout.cpp b/lib/IRGen/TypeLayout.cpp index 5ef1e9fbd1f60..88ba8adad4a35 100644 --- a/lib/IRGen/TypeLayout.cpp +++ b/lib/IRGen/TypeLayout.cpp @@ -79,11 +79,12 @@ class LayoutStringBuilder { SinglePayloadEnumFN = 0x11, // reserved // SinglePayloadEnumFNResolved = 0x12, + // SinglePayloadEnumGeneric = 0x13, - MultiPayloadEnumFN = 0x13, + MultiPayloadEnumFN = 0x14, // reserved - // MultiPayloadEnumFNResolved = 0x14, - // MultiPayloadEnumGeneric = 0x15, + // MultiPayloadEnumFNResolved = 0x15, + // MultiPayloadEnumGeneric = 0x16, Skip = 0x80, // We may use the MSB as flag that a count follows, diff --git a/stdlib/public/runtime/BytecodeLayouts.cpp b/stdlib/public/runtime/BytecodeLayouts.cpp index 64550ce438d41..32b4d2871db1d 100644 --- a/stdlib/public/runtime/BytecodeLayouts.cpp +++ b/stdlib/public/runtime/BytecodeLayouts.cpp @@ -117,6 +117,9 @@ inline static bool handleNextRefCount(const Metadata *metadata, RefCountingKind::SinglePayloadEnumFNResolved)) { Handler::handleSinglePayloadEnumFN(typeLayout, offset, true, addrOffset, std::forward(params)...); + } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumGeneric)) { + Handler::handleSinglePayloadEnumGeneric(typeLayout, offset, addrOffset, + std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::MultiPayloadEnumFN)) { Handler::handleMultiPayloadEnumFN(metadata, typeLayout, offset, false, addrOffset, @@ -126,8 +129,7 @@ inline static bool handleNextRefCount(const Metadata *metadata, Handler::handleMultiPayloadEnumFN(metadata, typeLayout, offset, true, addrOffset, std::forward(params)...); - } else if (SWIFT_UNLIKELY(tag == - RefCountingKind::MultiPayloadEnumGeneric)) { + } else if (SWIFT_UNLIKELY(tag == RefCountingKind::MultiPayloadEnumGeneric)) { Handler::handleMultiPayloadEnumGeneric(metadata, typeLayout, offset, addrOffset, std::forward(params)...); @@ -256,6 +258,51 @@ static void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset, } } +static void handleSinglePayloadEnumGeneric(const uint8_t *typeLayout, + size_t &offset, uint8_t *addr, + uintptr_t &addrOffset) { + auto tagBytesAndOffset = readBytes(typeLayout, offset); + auto extraTagBytesPattern = (uint8_t)(tagBytesAndOffset >> 62); + auto xiTagBytesOffset = + tagBytesAndOffset & std::numeric_limits::max(); + const Metadata *xiType = nullptr; + + if (extraTagBytesPattern) { + auto extraTagBytes = 1 << (extraTagBytesPattern - 1); + auto payloadSize = readBytes(typeLayout, offset); + auto tagBytes = + readTagBytes(addr + addrOffset + payloadSize, extraTagBytes); + if (tagBytes) { + offset += sizeof(uint64_t) + sizeof(size_t); + goto noPayload; + } + } else { + offset += sizeof(size_t); + } + + xiType = readBytes(typeLayout, offset); + + if (xiType) { + auto numEmptyCases = readBytes(typeLayout, offset); + + auto tag = xiType->vw_getEnumTagSinglePayload( + (const OpaqueValue *)(addr + addrOffset + xiTagBytesOffset), + numEmptyCases); + if (tag == 0) { + offset += sizeof(size_t) * 2; + return; + } + } else { + offset += sizeof(uint64_t) + sizeof(size_t); + } + +noPayload: + auto refCountBytes = readBytes(typeLayout, offset); + auto skip = readBytes(typeLayout, offset); + offset += refCountBytes; + addrOffset += skip; +} + template static void handleMultiPayloadEnumFN(const Metadata *metadata, const uint8_t *typeLayout, size_t &offset, @@ -363,6 +410,13 @@ struct DestroyHandler { ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, addr, addrOffset); } + static inline void handleSinglePayloadEnumGeneric(const uint8_t *typeLayout, + size_t &offset, + uintptr_t &addrOffset, + uint8_t *addr) { + ::handleSinglePayloadEnumGeneric(typeLayout, offset, addr, addrOffset); + } + static inline void handleMultiPayloadEnumFN(const Metadata *metadata, const uint8_t *typeLayout, size_t &offset, bool resolved, @@ -466,6 +520,14 @@ struct CopyHandler { ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, src, addrOffset); } + static inline void handleSinglePayloadEnumGeneric(const uint8_t *typeLayout, + size_t &offset, + uintptr_t &addrOffset, + uint8_t *dest, + uint8_t *src) { + ::handleSinglePayloadEnumGeneric(typeLayout, offset, src, addrOffset); + } + static inline void handleMultiPayloadEnumFN(const Metadata *metadata, const uint8_t *typeLayout, size_t &offset, bool resolved, @@ -533,6 +595,14 @@ struct TakeHandler { ::handleSinglePayloadEnumFN(typeLayout, offset, resolved, src, addrOffset); } + static inline void handleSinglePayloadEnumGeneric(const uint8_t *typeLayout, + size_t &offset, + uintptr_t &addrOffset, + uint8_t *dest, + uint8_t *src) { + ::handleSinglePayloadEnumGeneric(typeLayout, offset, src, addrOffset); + } + static inline void handleMultiPayloadEnumFN(const Metadata *metadata, const uint8_t *typeLayout, size_t &offset, bool resolved, diff --git a/stdlib/public/runtime/BytecodeLayouts.h b/stdlib/public/runtime/BytecodeLayouts.h index 2a4fafd742fcc..959ccb10d7a1f 100644 --- a/stdlib/public/runtime/BytecodeLayouts.h +++ b/stdlib/public/runtime/BytecodeLayouts.h @@ -46,10 +46,11 @@ enum class RefCountingKind : uint8_t { SinglePayloadEnumSimple = 0x10, SinglePayloadEnumFN = 0x11, SinglePayloadEnumFNResolved = 0x12, + SinglePayloadEnumGeneric = 0x13, - MultiPayloadEnumFN = 0x13, - MultiPayloadEnumFNResolved = 0x14, - MultiPayloadEnumGeneric = 0x15, + MultiPayloadEnumFN = 0x14, + MultiPayloadEnumFNResolved = 0x15, + MultiPayloadEnumGeneric = 0x16, Skip = 0x80, // We may use the MSB as flag that a count follows, diff --git a/stdlib/public/runtime/Enum.cpp b/stdlib/public/runtime/Enum.cpp index 38c4b316f19ab..68ec558cd7688 100644 --- a/stdlib/public/runtime/Enum.cpp +++ b/stdlib/public/runtime/Enum.cpp @@ -85,7 +85,9 @@ void swift::swift_initEnumMetadataSingleCaseWithLayoutString( uint8_t *layoutStr = (uint8_t *)MetadataAllocator(LayoutStringTag) - .Allocate(fixedLayoutStringSize + refCountBytes, alignof(uint8_t)); + .Allocate(llvm::alignTo(fixedLayoutStringSize + refCountBytes, + sizeof(void *)), + alignof(uint8_t)); size_t layoutStrOffset = sizeof(uint64_t); writeBytes(layoutStr, layoutStrOffset, refCountBytes); @@ -188,6 +190,172 @@ swift::swift_initEnumMetadataSinglePayload(EnumMetadata *self, vwtable->publishLayout(layout); } +namespace { +struct XIElement { + const Metadata *type; + size_t offset; +}; + +XIElement findXIElement(const Metadata *type) { + if (type->vw_getNumExtraInhabitants() == 0) { + return {nullptr, 0}; + } + + if (auto *tuple = dyn_cast(type)) { + assert(tuple->NumElements && + "Empty payloads can't store extra inhabitants"); + + const TupleTypeMetadata::Element *current = tuple->getElements(); + for (InProcess::StoredSize i = 1; i < tuple->NumElements; i++) { + auto &candidate = tuple->getElement(i); + if (current->Type->vw_getNumExtraInhabitants() < + candidate.Type->vw_getNumExtraInhabitants()) { + current = &candidate; + } + } + + return {current->Type, current->Offset}; + } else { + return {type, 0}; + } +} +} // namespace + +void swift::swift_initEnumMetadataSinglePayloadWithLayoutString( + EnumMetadata *self, EnumLayoutFlags layoutFlags, + const Metadata *payloadType, unsigned emptyCases) { + auto *payloadLayout = payloadType->getTypeLayout(); + size_t payloadSize = payloadLayout->size; + unsigned payloadNumExtraInhabitants = payloadLayout->getNumExtraInhabitants(); + + unsigned unusedExtraInhabitants = 0; + unsigned extraTagBytes = 0; + + // If there are enough extra inhabitants for all of the cases, then the size + // of the enum is the same as its payload. + size_t size; + if (payloadNumExtraInhabitants >= emptyCases) { + size = payloadSize; + unusedExtraInhabitants = payloadNumExtraInhabitants - emptyCases; + } else { + extraTagBytes = + getEnumTagCounts(payloadSize, emptyCases - payloadNumExtraInhabitants, + 1 /*payload case*/) + .numTagBytes; + size = payloadSize + extraTagBytes; + } + + auto vwtable = getMutableVWTableForInit(self, layoutFlags); + + size_t align = payloadLayout->flags.getAlignment(); + bool isBT = payloadLayout->flags.isBitwiseTakable(); + TypeLayout layout; + layout.size = size; + layout.flags = payloadLayout->flags.withEnumWitnesses(true).withInlineStorage( + ValueWitnessTable::isValueInline(isBT, size, align)); + layout.extraInhabitantCount = unusedExtraInhabitants; + auto rawStride = llvm::alignTo(size, align); + layout.stride = rawStride == 0 ? 1 : rawStride; + + auto xiElement = findXIElement(payloadType); + + size_t payloadRefCountBytes = _swift_refCountBytesForMetatype(payloadType); + size_t refCountBytes = payloadRefCountBytes + + sizeof(uint64_t) + // tag + offset + sizeof(uint64_t) + // extra tag bytes + XI offset + sizeof(size_t) + // payload size + sizeof(uintptr_t) + // XI metadata + sizeof(unsigned) + // num empty cases + sizeof(size_t) + // payload ref count bytes + sizeof(size_t); // bytes to skip if no payload case + + const size_t fixedLayoutStringSize = + layoutStringHeaderSize + + sizeof(uint64_t) * 2; // Last skip bytes + NUL terminator + + uint8_t *layoutStr = + (uint8_t *)MetadataAllocator(LayoutStringTag) + .Allocate(llvm::alignTo(fixedLayoutStringSize + refCountBytes, + sizeof(void *)), + alignof(uint8_t)); + + size_t layoutStrOffset = sizeof(uint64_t); + writeBytes(layoutStr, layoutStrOffset, refCountBytes); + + uint64_t tagAndOffset = ((uint64_t)RefCountingKind::SinglePayloadEnumGeneric) + << 56; + writeBytes(layoutStr, layoutStrOffset, tagAndOffset); + + uint64_t compactExtraTagByteCount = std::min(extraTagBytes, 3u); + writeBytes(layoutStr, layoutStrOffset, + compactExtraTagByteCount << 62 | xiElement.offset); + + writeBytes(layoutStr, layoutStrOffset, payloadSize); + + writeBytes(layoutStr, layoutStrOffset, xiElement.type); + writeBytes(layoutStr, layoutStrOffset, emptyCases); + writeBytes(layoutStr, layoutStrOffset, payloadRefCountBytes); + + // skip for now and fill in after writing the payload ref count string + auto skipBytesOffset = layoutStrOffset; + layoutStrOffset += sizeof(size_t); + + size_t fullOffset = 0; + size_t previousFieldOffset = 0; + LayoutStringFlags flags = LayoutStringFlags::Empty; + + _swift_addRefCountStringForMetatype(layoutStr, layoutStrOffset, flags, + payloadType, fullOffset, + previousFieldOffset); + + writeBytes(layoutStr, skipBytesOffset, size - previousFieldOffset); + + writeBytes(layoutStr, layoutStrOffset, (uint64_t)previousFieldOffset); + writeBytes(layoutStr, layoutStrOffset, (uint64_t)0); + + // we mask out HasRelativePointers, because at this point they have all been + // resolved to metadata pointers + layoutStrOffset = 0; + writeBytes(layoutStr, layoutStrOffset, + ((uint64_t)flags) & + ~((uint64_t)LayoutStringFlags::HasRelativePointers)); + + self->setLayoutString(layoutStr); + vwtable->destroy = swift_generic_destroy; + vwtable->initializeWithCopy = swift_generic_initWithCopy; + vwtable->initializeWithTake = swift_generic_initWithTake; + vwtable->assignWithCopy = swift_generic_assignWithCopy; + vwtable->assignWithTake = swift_generic_assignWithTake; + + // Substitute in better common value witnesses if we have them. + // If the payload type is a single-refcounted pointer, and the enum has + // a single empty case, then we can borrow the witnesses of the single + // refcounted pointer type, since swift_retain and objc_retain are both + // nil-aware. Most single-refcounted types will use the standard + // value witness tables for NativeObject or AnyObject. This isn't + // foolproof but should catch the common case of optional class types. +#if OPTIONAL_OBJECT_OPTIMIZATION + auto payloadVWT = payload->getValueWitnesses(); + if (emptyCases == 1 && (payloadVWT == &VALUE_WITNESS_SYM(Bo) +#if SWIFT_OBJC_INTEROP + || payloadVWT == &VALUE_WITNESS_SYM(BO) +#endif + )) { +#define WANT_ONLY_REQUIRED_VALUE_WITNESSES +#define VALUE_WITNESS(LOWER_ID, UPPER_ID) \ + vwtable->LOWER_ID = payloadVWT->LOWER_ID; +#define DATA_VALUE_WITNESS(LOWER_ID, UPPER_ID, TYPE) +#include "swift/ABI/ValueWitness.def" + } else { +#endif + installCommonValueWitnesses(layout, vwtable); +#if OPTIONAL_OBJECT_OPTIMIZATION + } +#endif + + vwtable->publishLayout(layout); +} + unsigned swift::swift_getEnumTagSinglePayloadGeneric(const OpaqueValue *value, unsigned emptyCases, @@ -326,8 +494,10 @@ void swift::swift_initEnumMetadataMultiPayloadWithLayoutString( payloadRefCountBytes + sizeof(uint64_t) * 2; // Last skip bytes + NUL terminator - uint8_t *layoutStr = (uint8_t *)MetadataAllocator(LayoutStringTag) - .Allocate(allocationSize, alignof(uint8_t)); + uint8_t *layoutStr = + (uint8_t *)MetadataAllocator(LayoutStringTag) + .Allocate(llvm::alignTo(allocationSize, sizeof(void *)), + alignof(uint8_t)); size_t layoutStrOffset = sizeof(uint64_t); uint64_t tagAndOffset = ((uint64_t)RefCountingKind::MultiPayloadEnumGeneric) << 56; diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index b2fb2d93368c7..765029e92c590 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2709,8 +2709,11 @@ void swift::swift_initStructMetadataWithLayoutString( const size_t fixedLayoutStringSize = layoutStringHeaderSize + sizeof(uint64_t) * 2; - uint8_t *layoutStr = (uint8_t *)MetadataAllocator(LayoutStringTag) - .Allocate(fixedLayoutStringSize + refCountBytes, alignof(uint8_t)); + uint8_t *layoutStr = + (uint8_t *)MetadataAllocator(LayoutStringTag) + .Allocate(llvm::alignTo(fixedLayoutStringSize + refCountBytes, + sizeof(void *)), + alignof(uint8_t)); size_t layoutStrOffset = sizeof(uint64_t); diff --git a/test/Interpreter/Inputs/layout_string_witnesses_types.swift b/test/Interpreter/Inputs/layout_string_witnesses_types.swift index 1eb45df5b93d2..7c49496535bad 100644 --- a/test/Interpreter/Inputs/layout_string_witnesses_types.swift +++ b/test/Interpreter/Inputs/layout_string_witnesses_types.swift @@ -299,7 +299,7 @@ public struct ContainsSinglePayloadSimpleClassEnum { public enum SinglePayloadEnum { case empty - case nonEmpty(T?) + case nonEmpty(Int, T?) } public struct SinglePayloadEnumWrapper { diff --git a/test/Interpreter/layout_string_witnesses_dynamic.swift b/test/Interpreter/layout_string_witnesses_dynamic.swift index 3bf1c64fa382b..49b98a2abd48d 100644 --- a/test/Interpreter/layout_string_witnesses_dynamic.swift +++ b/test/Interpreter/layout_string_witnesses_dynamic.swift @@ -460,6 +460,34 @@ func testMixedEnumWrapperWrapperGeneric() { testMixedEnumWrapperWrapperGeneric() +func testGenericSinglePayloadEnum() { + let ptr = allocateInternalGenericPtr(of: SinglePayloadEnum.self) + + do { + let x = SinglePayloadEnum.nonEmpty(23, SimpleClass(x: 23)) + testGenericInit(ptr, to: x) + } + + do { + let y = SinglePayloadEnum.nonEmpty(32, SimpleClass(x: 32)) + // CHECK: Before deinit + print("Before deinit") + + // CHECK-NEXT: SimpleClass deinitialized! + testGenericAssign(ptr, from: y) + } + + // CHECK-NEXT: Before deinit + print("Before deinit") + + // CHECK-NEXT: SimpleClass deinitialized! + testGenericDestroy(ptr, of: SinglePayloadEnum.self) + + ptr.deallocate() +} + +testGenericSinglePayloadEnum() + func testGenericSinglePayloadEnumManyXI() { let ptr = allocateInternalGenericPtr(of: SinglePayloadEnumManyXI.self)