From 8114ecc3e083167e782657303b9fe6daf2fb50f3 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Thu, 29 Jun 2023 14:44:27 -0700 Subject: [PATCH 1/2] [Runtime] Add destructiveInjectEnumTag support for generic multi payload enums with layout strings --- include/swift/Runtime/RuntimeFunctions.def | 11 +++++++ stdlib/public/runtime/BytecodeLayouts.cpp | 35 ++++++++++++++++++++++ stdlib/public/runtime/BytecodeLayouts.h | 3 ++ 3 files changed, 49 insertions(+) diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index ff8c945eb1e96..a48b50f98c672 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -2366,6 +2366,17 @@ FUNCTION(MultiPayloadEnumGenericGetEnumTag, ATTRS(NoUnwind, WillReturn), EFFECT(NoEffect)) +// void swift_multiPayloadEnumGeneric_destructiveInjectEnumTag(swift::OpaqueValue *address, +// unsigned tag, +// const Metadata *metadata) +FUNCTION(MultiPayloadEnumGenericDestructiveInjectEnumTag, + swift_multiPayloadEnumGeneric_destructiveInjectEnumTag, + C_CC, AlwaysAvailable, + RETURNS(VoidTy), + ARGS(Int8PtrTy, Int32Ty, TypeMetadataPtrTy), + ATTRS(NoUnwind, WillReturn), + EFFECT(NoEffect)) + // unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, // const Metadata *metadata); FUNCTION(SinglePayloadEnumGenericGetEnumTag, diff --git a/stdlib/public/runtime/BytecodeLayouts.cpp b/stdlib/public/runtime/BytecodeLayouts.cpp index cadd660b15590..aba790330961a 100644 --- a/stdlib/public/runtime/BytecodeLayouts.cpp +++ b/stdlib/public/runtime/BytecodeLayouts.cpp @@ -746,6 +746,41 @@ swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, } } +extern "C" void swift_multiPayloadEnumGeneric_destructiveInjectEnumTag( + swift::OpaqueValue *address, unsigned tag, const Metadata *metadata) { + auto addr = reinterpret_cast(address); + LayoutStringReader reader{metadata->getLayoutString(), + layoutStringHeaderSize + sizeof(uint64_t)}; + + auto numTagBytes = reader.readBytes(); + auto numPayloads = reader.readBytes(); + reader.skip(sizeof(size_t)); + auto enumSize = reader.readBytes(); + auto payloadSize = enumSize - numTagBytes; + + if (tag < numPayloads) { + // For a payload case, store the tag after the payload area. + auto tagBytes = addr + payloadSize; + storeEnumElement(tagBytes, tag, numTagBytes); + } else { + // For an empty case, factor out the parts that go in the payload and + // tag areas. + unsigned whichEmptyCase = tag - numPayloads; + unsigned whichTag, whichPayloadValue; + if (payloadSize >= 4) { + whichTag = numPayloads; + whichPayloadValue = whichEmptyCase; + } else { + unsigned numPayloadBits = payloadSize * CHAR_BIT; + whichTag = numPayloads + (whichEmptyCase >> numPayloadBits); + whichPayloadValue = whichEmptyCase & ((1U << numPayloadBits) - 1U); + } + auto tagBytes = addr + payloadSize; + storeEnumElement(tagBytes, whichTag, numTagBytes); + storeEnumElement(addr, whichPayloadValue, payloadSize); + } +} + template static inline T handleSinglePayloadEnumGenericTag( LayoutStringReader &reader, uint8_t *addr, diff --git a/stdlib/public/runtime/BytecodeLayouts.h b/stdlib/public/runtime/BytecodeLayouts.h index 7505bbf2f633a..a4a8890fdbe10 100644 --- a/stdlib/public/runtime/BytecodeLayouts.h +++ b/stdlib/public/runtime/BytecodeLayouts.h @@ -130,6 +130,9 @@ SWIFT_RUNTIME_EXPORT unsigned swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata); SWIFT_RUNTIME_EXPORT +void swift_multiPayloadEnumGeneric_destructiveInjectEnumTag( + swift::OpaqueValue *address, unsigned tag, const Metadata *metadata); +SWIFT_RUNTIME_EXPORT unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata); SWIFT_RUNTIME_EXPORT From 707330c8bd9bad877ec81fd3cf43e83ecc7f622b Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Thu, 29 Jun 2023 14:45:12 -0700 Subject: [PATCH 2/2] [IRGen] Emit swift_multiPayloadEnumGeneric_destructiveInjectEnumTag Assign swift_multiPayloadEnumGeneric_destructiveInjectEnumTag as witness function on generic multi payload enums when using layout strings --- lib/IRGen/GenValueWitness.cpp | 7 +++-- .../layout_string_witnesses_dynamic.swift | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 9d6e1aab148c8..4e7204b88bdc2 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -936,12 +936,13 @@ getDestructiveInjectEnumTagFunction(IRGenModule &IGM, const EnumTypeLayoutEntry *typeLayoutEntry, GenericSignature genericSig) { if ((!typeLayoutEntry->layoutString(IGM, genericSig) && - !isRuntimeInstatiatedLayoutString(IGM, typeLayoutEntry)) || - typeLayoutEntry->isSingleton()) { + !isRuntimeInstatiatedLayoutString(IGM, typeLayoutEntry))) { + return nullptr; + } else if (typeLayoutEntry->isSingleton()) { return nullptr; } else if (!typeLayoutEntry->isFixedSize(IGM)) { if (typeLayoutEntry->isMultiPayloadEnum()) { - return nullptr; + return IGM.getMultiPayloadEnumGenericDestructiveInjectEnumTagFn(); } else { return IGM.getSinglePayloadEnumGenericDestructiveInjectEnumTagFn(); } diff --git a/test/Interpreter/layout_string_witnesses_dynamic.swift b/test/Interpreter/layout_string_witnesses_dynamic.swift index 463022bd21436..ef0e606f09a8b 100644 --- a/test/Interpreter/layout_string_witnesses_dynamic.swift +++ b/test/Interpreter/layout_string_witnesses_dynamic.swift @@ -618,6 +618,34 @@ func testResilientSinglePayloadEnumGenericInjectTag() { testResilientSinglePayloadEnumGenericInjectTag() +@inline(never) +func matchResilientMultiPayloadEnumGenericTag(_ x: ResilientMultiPayloadEnumGeneric) -> Int { + return switch x { + case .nonEmpty0: 0 + case .nonEmpty1: 1 + case .empty0: 2 + case .empty1: 3 + } +} + +func testResilientMultiPayloadEnumGenericInjectTag() { + let x = ResilientMultiPayloadEnumGeneric.nonEmpty0(SimpleClass(x: 23)) + let y = ResilientMultiPayloadEnumGeneric.nonEmpty1(SimpleClass(x: 32)) + let z = ResilientMultiPayloadEnumGeneric.empty0 + let w = ResilientMultiPayloadEnumGeneric.empty1 + + // CHECK: Enum case: 0 + print("Enum case: \(matchResilientMultiPayloadEnumGenericTag(x))") + // CHECK: Enum case: 1 + print("Enum case: \(matchResilientMultiPayloadEnumGenericTag(y))") + // CHECK: Enum case: 2 + print("Enum case: \(matchResilientMultiPayloadEnumGenericTag(z))") + // CHECK: Enum case: 3 + print("Enum case: \(matchResilientMultiPayloadEnumGenericTag(w))") +} + +testResilientMultiPayloadEnumGenericInjectTag() + #if os(macOS) import Foundation