//===--- RuntimeValueWitness.cpp - Value Witness Runtime Implementation---===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // Implementations of runtime determined value witness functions // This file is intended to be statically linked into executables until it is // fully added to the runtime. // //===----------------------------------------------------------------------===// #include "BytecodeLayouts.h" #include "../SwiftShims/swift/shims/HeapObject.h" #include "EnumImpl.h" #include "WeakReference.h" #include "swift/ABI/MetadataValues.h" #include "swift/ABI/System.h" #include "swift/Runtime/Error.h" #include "swift/Runtime/HeapObject.h" #include "llvm/Support/SwapByteOrder.h" #include #include #include #include #include #if SWIFT_OBJC_INTEROP #include "swift/Runtime/ObjCBridge.h" #include #endif #if SWIFT_PTRAUTH #include #endif using namespace swift; static Metadata *getExistentialTypeMetadata(OpaqueValue *object) { return reinterpret_cast(object)[NumWords_ValueBuffer]; } template static const FnTy readRelativeFunctionPointer(LayoutStringReader &reader) { static_assert(std::is_pointer::value); auto absolute = reader.layoutStr + reader.offset; auto relativeOffset = (uintptr_t)(intptr_t)(int32_t)reader.readBytes(); FnTy fn; #if SWIFT_PTRAUTH fn = (FnTy)ptrauth_sign_unauthenticated( (void *)((uintptr_t)absolute + relativeOffset), ptrauth_key_function_pointer, 0); #else fn = (FnTy)((uintptr_t)absolute + relativeOffset); #endif return fn; } typedef Metadata *(*MetadataAccessor)(const Metadata *const *); static const Metadata *getResilientTypeMetadata(const Metadata *metadata, LayoutStringReader &reader) { auto fn = readRelativeFunctionPointer(reader); return fn(metadata->getGenericArgs()); } typedef void (*DestrFn)(void*); struct DestroyFuncAndMask { DestrFn fn; bool isIndirect; }; static void skipDestroy(void* ignore) { } static void existential_destroy(OpaqueValue* object) { auto* metadata = getExistentialTypeMetadata(object); if (metadata->getValueWitnesses()->isValueInline()) { metadata->vw_destroy(object); } else { swift_release(*(HeapObject**)object); } } template inline static bool handleNextRefCount(const Metadata *metadata, LayoutStringReader &reader, uintptr_t &addrOffset, Params... params) { uint64_t skip = reader.readBytes(); auto tag = static_cast(skip >> 56); skip &= ~(0xffULL << 56); addrOffset += skip; if (SWIFT_UNLIKELY(tag == RefCountingKind::End)) { return false; } else if (SWIFT_UNLIKELY(tag == RefCountingKind::Metatype)) { auto *type = reader.readBytes(); Handler::handleMetatype(type, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::Resilient)) { auto *type = getResilientTypeMetadata(metadata, reader); Handler::handleMetatype(type, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumSimple)) { Handler::handleSinglePayloadEnumSimple(reader, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumFN)) { Handler::handleSinglePayloadEnumFN(reader, false, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumFNResolved)) { Handler::handleSinglePayloadEnumFN(reader, true, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumGeneric)) { Handler::handleSinglePayloadEnumGeneric(reader, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::MultiPayloadEnumFN)) { Handler::handleMultiPayloadEnumFN(metadata, reader, false, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::MultiPayloadEnumFNResolved)) { Handler::handleMultiPayloadEnumFN(metadata, reader, true, addrOffset, std::forward(params)...); } else if (SWIFT_UNLIKELY(tag == RefCountingKind::MultiPayloadEnumGeneric)) { Handler::handleMultiPayloadEnumGeneric(metadata, reader, addrOffset, std::forward(params)...); } else { Handler::handleReference(tag, addrOffset, std::forward(params)...); } return true; } template inline static void handleRefCounts(const Metadata *metadata, LayoutStringReader &reader, uintptr_t &addrOffset, Params... params) { if (N == 0) { while (handleNextRefCount(metadata, reader, addrOffset, std::forward(params)...)) { } } else { for (unsigned i = 0; i < N; i++) { handleNextRefCount(metadata, reader, addrOffset, std::forward(params)...); } } } template inline static void handleRefCounts(const Metadata *metadata, Params... params) { LayoutStringReader reader{metadata->getLayoutString(), layoutStringHeaderSize}; uintptr_t addrOffset = 0; handleRefCounts(metadata, reader, addrOffset, std::forward(params)...); } static uint64_t readTagBytes(const uint8_t *addr, uint8_t byteCount) { switch (byteCount) { case 1: return addr[0]; case 2: { uint16_t res = 0; memcpy(&res, addr, sizeof(uint16_t)); return res; } case 4: { uint32_t res = 0; memcpy(&res, addr, sizeof(uint32_t)); return res; } case 8: { uint64_t res = 0; memcpy(&res, addr, sizeof(uint64_t)); return res; } default: swift_unreachable("Unsupported tag byte length."); } } static void handleSinglePayloadEnumSimple(LayoutStringReader &reader, uint8_t *addr, uintptr_t &addrOffset) { auto byteCountsAndOffset = reader.readBytes(); auto extraTagBytesPattern = (uint8_t)(byteCountsAndOffset >> 62); auto xiTagBytesPattern = ((uint8_t)(byteCountsAndOffset >> 59)) & 0x7; auto xiTagBytesOffset = byteCountsAndOffset & std::numeric_limits::max(); if (extraTagBytesPattern) { auto extraTagBytes = 1 << (extraTagBytesPattern - 1); auto payloadSize = reader.readBytes(); auto tagBytes = readTagBytes(addr + addrOffset + payloadSize, extraTagBytes); if (tagBytes) { reader.skip(sizeof(uint64_t) + sizeof(size_t)); goto noPayload; } } else { reader.skip(sizeof(size_t)); } if (xiTagBytesPattern) { auto zeroTagValue = reader.readBytes(); auto xiTagValues = reader.readBytes(); auto xiTagBytes = 1 << (xiTagBytesPattern - 1); uint64_t tagBytes = readTagBytes(addr + addrOffset + xiTagBytesOffset, xiTagBytes) - zeroTagValue; if (tagBytes >= xiTagValues) { reader.skip(sizeof(size_t) * 2); return; } } else { reader.skip(sizeof(uint64_t) + sizeof(size_t)); } noPayload: auto refCountBytes = reader.readBytes(); auto skip = reader.readBytes(); reader.skip(refCountBytes); addrOffset += skip; } typedef unsigned (*GetEnumTagFn)(const uint8_t *); static void handleSinglePayloadEnumFN(LayoutStringReader &reader, bool resolved, uint8_t *addr, uintptr_t &addrOffset) { GetEnumTagFn getEnumTag; if (resolved) { getEnumTag = reader.readBytes(); } else { getEnumTag = readRelativeFunctionPointer(reader); } unsigned enumTag = getEnumTag(addr + addrOffset); if (enumTag == 0) { reader.skip(sizeof(size_t) * 2); } else { auto refCountBytes = reader.readBytes(); auto skip = reader.readBytes(); reader.skip(refCountBytes); addrOffset += skip; } } static void handleSinglePayloadEnumGeneric(LayoutStringReader &reader, uint8_t *addr, uintptr_t &addrOffset) { auto tagBytesAndOffset = reader.readBytes(); 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 = reader.readBytes(); auto tagBytes = readTagBytes(addr + addrOffset + payloadSize, extraTagBytes); if (tagBytes) { reader.skip(sizeof(uint64_t) + sizeof(size_t)); goto noPayload; } } else { reader.skip(sizeof(size_t)); } xiType = reader.readBytes(); if (xiType) { auto numEmptyCases = reader.readBytes(); auto tag = xiType->vw_getEnumTagSinglePayload( (const OpaqueValue *)(addr + addrOffset + xiTagBytesOffset), numEmptyCases); if (tag == 0) { reader.skip(sizeof(size_t) * 2); return; } } else { reader.skip(sizeof(uint64_t) + sizeof(size_t)); } noPayload: auto refCountBytes = reader.readBytes(); auto skip = reader.readBytes(); reader.skip(refCountBytes); addrOffset += skip; } template static void handleMultiPayloadEnumFN(const Metadata *metadata, LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *addr, Params... params) { GetEnumTagFn getEnumTag; if (resolved) { getEnumTag = reader.readBytes(); } else { getEnumTag = readRelativeFunctionPointer(reader); } size_t numPayloads = reader.readBytes(); size_t refCountBytes = reader.readBytes(); size_t enumSize = reader.readBytes(); unsigned enumTag = getEnumTag(addr + addrOffset); if (enumTag < numPayloads) { size_t refCountOffset = reader.peekBytes(enumTag * sizeof(size_t)); LayoutStringReader nestedReader = reader; nestedReader.skip((numPayloads * sizeof(size_t)) + refCountOffset); uintptr_t nestedAddrOffset = addrOffset; handleRefCounts<0, Handler>(metadata, nestedReader, nestedAddrOffset, addr, std::forward(params)...); } reader.skip(refCountBytes + (numPayloads * sizeof(size_t))); addrOffset += enumSize; } template static void handleMultiPayloadEnumGeneric(const Metadata *metadata, LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *addr, Params... params) { auto tagBytes = reader.readBytes(); auto numPayloads = reader.readBytes(); auto refCountBytes = reader.readBytes(); auto enumSize = reader.readBytes(); auto tagBytesOffset = enumSize - tagBytes; auto enumTag = readTagBytes(addr + addrOffset + tagBytesOffset, tagBytes); if (enumTag < numPayloads) { size_t refCountOffset = reader.peekBytes(enumTag * sizeof(size_t)); LayoutStringReader nestedReader = reader; nestedReader.skip((numPayloads * sizeof(size_t)) + refCountOffset); uintptr_t nestedAddrOffset = addrOffset; handleRefCounts<0, Handler>(metadata, nestedReader, nestedAddrOffset, addr, std::forward(params)...); } reader.skip(refCountBytes + (numPayloads * sizeof(size_t))); addrOffset += enumSize; } const DestroyFuncAndMask destroyTable[] = { {(DestrFn)&skipDestroy, false}, {(DestrFn)&swift_errorRelease, true}, {(DestrFn)&swift_release, true}, {(DestrFn)&swift_unownedRelease, true}, {(DestrFn)&swift_weakDestroy, false}, {(DestrFn)&swift_unknownObjectRelease, true}, {(DestrFn)&swift_unknownObjectUnownedDestroy, false}, {(DestrFn)&swift_unknownObjectWeakDestroy, false}, {(DestrFn)&swift_bridgeObjectRelease, true}, #if SWIFT_OBJC_INTEROP {(DestrFn)&_Block_release, true}, {(DestrFn)&swift_unknownObjectRelease, true}, #else {nullptr, true}, {nullptr, true}, #endif // TODO: how to handle Custom? {nullptr, true}, {nullptr, true}, {nullptr, true}, {(DestrFn)&existential_destroy, false}, }; struct DestroyHandler { static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, uint8_t *addr) { type->vw_destroy((OpaqueValue *)(addr + addrOffset)); } static inline void handleSinglePayloadEnumSimple(LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *addr) { ::handleSinglePayloadEnumSimple(reader, addr, addrOffset); } static inline void handleSinglePayloadEnumFN(LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *addr) { ::handleSinglePayloadEnumFN(reader, resolved, addr, addrOffset); } static inline void handleSinglePayloadEnumGeneric(LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *addr) { ::handleSinglePayloadEnumGeneric(reader, addr, addrOffset); } static inline void handleMultiPayloadEnumFN(const Metadata *metadata, LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *addr) { ::handleMultiPayloadEnumFN(metadata, reader, resolved, addrOffset, addr); } static inline void handleMultiPayloadEnumGeneric(const Metadata *metadata, LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *addr) { ::handleMultiPayloadEnumGeneric(metadata, reader, addrOffset, addr); } static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *addr) { const auto &destroyFunc = destroyTable[static_cast(tag)]; if (SWIFT_LIKELY(destroyFunc.isIndirect)) { destroyFunc.fn( (void *)((*(uintptr_t *)(addr + addrOffset)))); } else { destroyFunc.fn(((void *)(addr + addrOffset))); } } }; extern "C" void swift_generic_destroy(swift::OpaqueValue *address, const Metadata *metadata) { handleRefCounts<0, DestroyHandler>(metadata, (uint8_t *)address); } struct RetainFuncAndMask { void* fn; bool isSingle; }; #if SWIFT_OBJC_INTEROP void* Block_copyForwarder(void** dest, const void** src) { *dest = _Block_copy(*src); return *dest; } #endif typedef void* (*RetainFn)(void*); typedef void* (*CopyInitFn)(void*, void*); void* skipRetain(void* ignore) { return nullptr; } void* existential_initializeWithCopy(OpaqueValue* dest, OpaqueValue* src) { auto* metadata = getExistentialTypeMetadata(src); return metadata->vw_initializeBufferWithCopyOfBuffer((ValueBuffer*)dest, (ValueBuffer*)src); } const RetainFuncAndMask retainTable[] = { {(void*)&skipRetain, true}, {(void*)&swift_errorRetain, true}, {(void*)&swift_retain, true}, {(void*)&swift_unownedRetain, true}, {(void*)&swift_weakCopyInit, false}, {(void*)&swift_unknownObjectRetain, true}, {(void*)&swift_unknownObjectUnownedCopyInit, false}, {(void*)&swift_unknownObjectWeakCopyInit, false}, {(void*)&swift_bridgeObjectRetain, true}, #if SWIFT_OBJC_INTEROP {(void*)&Block_copyForwarder, false}, {(void*)&objc_retain, true}, #else {nullptr, true}, {nullptr, true}, #endif // TODO: how to handle Custom? {nullptr, true}, {nullptr, true}, {nullptr, true}, {(void*)&existential_initializeWithCopy, false}, }; struct CopyHandler { static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { type->vw_initializeWithCopy((OpaqueValue*)((uintptr_t)dest + addrOffset), (OpaqueValue*)((uintptr_t)src + addrOffset)); } static inline void handleSinglePayloadEnumSimple(LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleSinglePayloadEnumSimple(reader, src, addrOffset); } static inline void handleSinglePayloadEnumFN(LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleSinglePayloadEnumFN(reader, resolved, src, addrOffset); } static inline void handleSinglePayloadEnumGeneric(LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleSinglePayloadEnumGeneric(reader, src, addrOffset); } static inline void handleMultiPayloadEnumFN(const Metadata *metadata, LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleMultiPayloadEnumFN(metadata, reader, resolved, addrOffset, dest, src); } static inline void handleMultiPayloadEnumGeneric(const Metadata *metadata, LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleMultiPayloadEnumGeneric(metadata, reader, addrOffset, dest, src); } static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { const auto &retainFunc = retainTable[static_cast(tag)]; if (SWIFT_LIKELY(retainFunc.isSingle)) { ((RetainFn)retainFunc.fn)(*(void**)(((uintptr_t)dest + addrOffset))); } else { ((CopyInitFn)retainFunc.fn)((void*)((uintptr_t)dest + addrOffset), (void*)((uintptr_t)src + addrOffset)); } } }; extern "C" swift::OpaqueValue * swift_generic_initWithCopy(swift::OpaqueValue *dest, swift::OpaqueValue *src, const Metadata *metadata) { size_t size = metadata->vw_size(); memcpy(dest, src, size); handleRefCounts<0, CopyHandler>(metadata, (uint8_t *)dest, (uint8_t *)src); return dest; } struct TakeHandler { static inline void handleMetatype(const Metadata *type, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { if (SWIFT_UNLIKELY(!type->getValueWitnesses()->isBitwiseTakable())) { type->vw_initializeWithTake( (OpaqueValue*)((uintptr_t)dest + addrOffset), (OpaqueValue*)((uintptr_t)src + addrOffset)); } } static inline void handleSinglePayloadEnumSimple(LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleSinglePayloadEnumSimple(reader, src, addrOffset); } static inline void handleSinglePayloadEnumFN(LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleSinglePayloadEnumFN(reader, resolved, src, addrOffset); } static inline void handleSinglePayloadEnumGeneric(LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleSinglePayloadEnumGeneric(reader, src, addrOffset); } static inline void handleMultiPayloadEnumFN(const Metadata *metadata, LayoutStringReader &reader, bool resolved, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleMultiPayloadEnumFN(metadata, reader, resolved, addrOffset, dest, src); } static inline void handleMultiPayloadEnumGeneric(const Metadata *metadata, LayoutStringReader &reader, uintptr_t &addrOffset, uint8_t *dest, uint8_t *src) { ::handleMultiPayloadEnumGeneric(metadata, reader, addrOffset, dest, src); } static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) { if (tag == RefCountingKind::UnknownWeak) { swift_unknownObjectWeakTakeInit( (WeakReference*)((uintptr_t)dest + addrOffset), (WeakReference*)((uintptr_t)src + addrOffset)); } else if (tag == RefCountingKind::Existential) { auto *type = getExistentialTypeMetadata( (OpaqueValue*)((uintptr_t)src + addrOffset)); if (SWIFT_UNLIKELY(!type->getValueWitnesses()->isBitwiseTakable())) { type->vw_initializeWithTake( (OpaqueValue *)((uintptr_t)dest + addrOffset), (OpaqueValue *)((uintptr_t)src + addrOffset)); } } } }; extern "C" swift::OpaqueValue * swift_generic_initWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src, const Metadata *metadata) { size_t size = metadata->vw_size(); memcpy(dest, src, size); if (SWIFT_LIKELY(metadata->getValueWitnesses()->isBitwiseTakable())) { return dest; } handleRefCounts<0, TakeHandler>(metadata, (uint8_t *)dest, (uint8_t *)src); return dest; } extern "C" swift::OpaqueValue * swift_generic_assignWithCopy(swift::OpaqueValue *dest, swift::OpaqueValue *src, const Metadata *metadata) { swift_generic_destroy(dest, metadata); return swift_generic_initWithCopy(dest, src, metadata); } extern "C" swift::OpaqueValue * swift_generic_assignWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src, const Metadata *metadata) { swift_generic_destroy(dest, metadata); return swift_generic_initWithTake(dest, src, metadata); } extern "C" unsigned swift_singletonEnum_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata) { return 0; } extern "C" void swift_singletonEnum_destructiveInjectEnumTag( swift::OpaqueValue *address, unsigned tag, const Metadata *metadata) { return; } template static inline T handleSinglePayloadEnumSimpleTag( LayoutStringReader &reader, uint8_t *addr, std::function(size_t, size_t, uint8_t)> extraTagBytesHandler, std::function xiHandler) { auto byteCountsAndOffset = reader.readBytes(); auto extraTagBytesPattern = (uint8_t)(byteCountsAndOffset >> 62); auto xiTagBytesPattern = ((uint8_t)(byteCountsAndOffset >> 59)) & 0x7; auto xiTagBytesOffset = byteCountsAndOffset & std::numeric_limits::max(); auto numExtraTagBytes = 1 << (extraTagBytesPattern - 1); auto payloadSize = reader.readBytes(); auto zeroTagValue = reader.readBytes(); auto payloadNumExtraInhabitants = reader.readBytes(); if (extraTagBytesPattern) { if (auto result = extraTagBytesHandler(payloadNumExtraInhabitants, payloadSize, numExtraTagBytes)) { return *result; } } return xiHandler(payloadNumExtraInhabitants, zeroTagValue, xiTagBytesPattern, xiTagBytesOffset, payloadSize, numExtraTagBytes); } extern "C" unsigned swift_enumSimple_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata) { auto addr = reinterpret_cast(address); LayoutStringReader reader{metadata->getLayoutString(), layoutStringHeaderSize + sizeof(uint64_t)}; auto extraTagBytesHandler = [addr](size_t payloadNumExtraInhabitants, size_t payloadSize, uint8_t numExtraTagBytes) -> std::optional { auto tagBytes = readTagBytes(addr + payloadSize, numExtraTagBytes); if (tagBytes) { unsigned caseIndexFromExtraTagBits = payloadSize >= 4 ? 0 : (tagBytes - 1U) << (payloadSize * 8U); unsigned caseIndexFromValue = loadEnumElement(addr, payloadSize); unsigned noPayloadIndex = (caseIndexFromExtraTagBits | caseIndexFromValue) + payloadNumExtraInhabitants; return noPayloadIndex + 1; } return std::nullopt; }; auto xihandler = [addr](size_t payloadNumExtraInhabitants, uint64_t zeroTagValue, uint8_t xiTagBytesPattern, unsigned xiTagBytesOffset, size_t payloadSize, uint8_t numExtraTagBytes) -> unsigned { auto xiTagBytes = 1 << (xiTagBytesPattern - 1); uint64_t tagBytes = readTagBytes(addr + xiTagBytesOffset, xiTagBytes) - zeroTagValue; if (tagBytes < payloadNumExtraInhabitants) { return tagBytes + 1; } return 0; }; return handleSinglePayloadEnumSimpleTag( reader, addr, extraTagBytesHandler, xihandler); } extern "C" unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata) { auto addr = reinterpret_cast(address); LayoutStringReader reader{metadata->getLayoutString(), layoutStringHeaderSize + sizeof(uint64_t)}; auto getEnumTag = readRelativeFunctionPointer(reader); return getEnumTag(addr); } extern "C" unsigned swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata) { auto addr = reinterpret_cast(address); LayoutStringReader reader{metadata->getLayoutString(), layoutStringHeaderSize + sizeof(uint64_t)}; auto tagBytes = reader.readBytes(); auto numPayloads = reader.readBytes(); reader.skip(sizeof(size_t)); auto enumSize = reader.readBytes(); auto payloadSize = enumSize - tagBytes; auto enumTag = (unsigned)readTagBytes(addr + payloadSize, tagBytes); if (enumTag < numPayloads) { return enumTag; } auto payloadValue = loadEnumElement(addr, payloadSize); if (payloadSize >= 4) { return numPayloads + payloadValue; } else { unsigned numPayloadBits = payloadSize * CHAR_BIT; return (payloadValue | (enumTag - numPayloads) << numPayloadBits) + numPayloads; } } 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, std::function(const Metadata *, size_t, uint8_t)> extraTagBytesHandler, std::function xiHandler) { auto tagBytesAndOffset = reader.readBytes(); auto extraTagBytesPattern = (uint8_t)(tagBytesAndOffset >> 62); auto xiTagBytesOffset = tagBytesAndOffset & std::numeric_limits::max(); auto numExtraTagBytes = 1 << (extraTagBytesPattern - 1); auto payloadSize = reader.readBytes(); auto xiType = reader.readBytes(); if (extraTagBytesPattern) { if (auto result = extraTagBytesHandler(xiType, payloadSize, numExtraTagBytes)) { return *result; } } auto numEmptyCases = reader.readBytes(); return xiHandler(xiType, xiTagBytesOffset, numEmptyCases, payloadSize, numExtraTagBytes); } extern "C" unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata) { auto addr = reinterpret_cast(address); LayoutStringReader reader{metadata->getLayoutString(), layoutStringHeaderSize + sizeof(uint64_t)}; auto extraTagBytesHandler = [addr](const Metadata *xiType, size_t payloadSize, uint8_t numExtraTagBytes) -> std::optional { auto tagBytes = readTagBytes(addr + payloadSize, numExtraTagBytes); if (tagBytes) { unsigned payloadNumExtraInhabitants = xiType ? xiType->vw_getNumExtraInhabitants() : 0; unsigned caseIndexFromExtraTagBits = payloadSize >= 4 ? 0 : (tagBytes - 1U) << (payloadSize * 8U); unsigned caseIndexFromValue = loadEnumElement(addr, payloadSize); unsigned noPayloadIndex = (caseIndexFromExtraTagBits | caseIndexFromValue) + payloadNumExtraInhabitants; return noPayloadIndex + 1; } return std::nullopt; }; auto xihandler = [addr](const Metadata *xiType, unsigned xiTagBytesOffset, unsigned numEmptyCases, size_t payloadSize, uint8_t numExtraTagBytes) -> unsigned { if (xiType) { return xiType->vw_getEnumTagSinglePayload( (const OpaqueValue *)(addr + xiTagBytesOffset), numEmptyCases); } return 0; }; return handleSinglePayloadEnumGenericTag( reader, addr, extraTagBytesHandler, xihandler); } extern "C" void swift_singlePayloadEnumGeneric_destructiveInjectEnumTag( swift::OpaqueValue *address, unsigned tag, const Metadata *metadata) { auto addr = reinterpret_cast(address); LayoutStringReader reader{metadata->getLayoutString(), layoutStringHeaderSize + sizeof(uint64_t)}; auto extraTagBytesHandler = [=](const Metadata *xiType, size_t payloadSize, uint8_t numExtraTagBytes) -> std::optional { unsigned payloadNumExtraInhabitants = xiType ? xiType->vw_getNumExtraInhabitants() : 0; if (tag <= payloadNumExtraInhabitants) { return std::nullopt; } unsigned noPayloadIndex = tag - 1; unsigned caseIndex = noPayloadIndex - payloadNumExtraInhabitants; unsigned payloadIndex, extraTagIndex; if (payloadSize >= 4) { extraTagIndex = 1; payloadIndex = caseIndex; } else { unsigned payloadBits = payloadSize * 8U; extraTagIndex = 1U + (caseIndex >> payloadBits); payloadIndex = caseIndex & ((1U << payloadBits) - 1U); } // Store into the value. if (payloadSize) storeEnumElement(addr, payloadIndex, payloadSize); if (numExtraTagBytes) storeEnumElement(addr + payloadSize, extraTagIndex, numExtraTagBytes); return true; }; auto xihandler = [=](const Metadata *xiType, unsigned xiTagBytesOffset, unsigned numEmptyCases, size_t payloadSize, uint8_t numExtraTagBytes) -> bool { unsigned payloadNumExtraInhabitants = xiType ? xiType->vw_getNumExtraInhabitants() : 0; if (tag <= payloadNumExtraInhabitants) { if (numExtraTagBytes != 0) storeEnumElement(addr + payloadSize, 0, numExtraTagBytes); if (tag == 0) return true; xiType->vw_storeEnumTagSinglePayload( (swift::OpaqueValue *)(addr + xiTagBytesOffset), tag, numEmptyCases); } return true; }; handleSinglePayloadEnumGenericTag(reader, addr, extraTagBytesHandler, xihandler); } extern "C" swift::OpaqueValue * swift_generic_initializeBufferWithCopyOfBuffer(swift::ValueBuffer *dest, swift::ValueBuffer *src, const Metadata *metadata) { if (metadata->getValueWitnesses()->isValueInline()) { return swift_generic_initWithCopy((swift::OpaqueValue *)dest, (swift::OpaqueValue *)src, metadata); } else { memcpy(dest, src, sizeof(swift::HeapObject *)); swift_retain(*(swift::HeapObject **)src); return (swift::OpaqueValue *)&(*(swift::HeapObject **)dest)[1]; } } void swift::swift_resolve_resilientAccessors(uint8_t *layoutStr, size_t layoutStrOffset, const uint8_t *fieldLayoutStr, const Metadata *fieldType) { LayoutStringWriter writer{layoutStr, layoutStrOffset}; LayoutStringReader reader{fieldLayoutStr, layoutStringHeaderSize}; while (true) { size_t currentOffset = reader.offset; uint64_t size = reader.readBytes(); RefCountingKind tag = (RefCountingKind)(size >> 56); size &= ~(0xffULL << 56); switch (tag) { case RefCountingKind::End: return; case RefCountingKind::Resilient: { auto *type = getResilientTypeMetadata(fieldType, reader); writer.offset = layoutStrOffset + currentOffset - layoutStringHeaderSize; uint64_t tagAndOffset = (((uint64_t)RefCountingKind::Metatype) << 56) | size; writer.writeBytes(tagAndOffset); writer.writeBytes(type); break; } case RefCountingKind::Metatype: reader.skip(sizeof(uintptr_t)); break; case RefCountingKind::SinglePayloadEnumSimple: reader.skip((3 * sizeof(uint64_t)) + (4 * sizeof(size_t))); break; case RefCountingKind::SinglePayloadEnumFN: { auto getEnumTag = readRelativeFunctionPointer(reader); writer.offset = layoutStrOffset + currentOffset - layoutStringHeaderSize; uint64_t tagAndOffset = (((uint64_t)RefCountingKind::SinglePayloadEnumFNResolved) << 56) | size; writer.writeBytes(tagAndOffset); writer.writeBytes(getEnumTag); reader.skip(2 * sizeof(size_t)); break; } case RefCountingKind::SinglePayloadEnumFNResolved: reader.skip(3 * sizeof(size_t)); break; case RefCountingKind::SinglePayloadEnumGeneric: { reader.skip(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 auto refCountBytes = reader.readBytes(); reader.skip(sizeof(size_t) + // bytes to skip if no payload case refCountBytes); break; } case RefCountingKind::MultiPayloadEnumFN: { auto getEnumTag = readRelativeFunctionPointer(reader); writer.offset = layoutStrOffset + currentOffset - layoutStringHeaderSize; uint64_t tagAndOffset = (((uint64_t)RefCountingKind::MultiPayloadEnumFNResolved) << 56) | size; writer.writeBytes(tagAndOffset); writer.writeBytes(getEnumTag); size_t numCases = reader.readBytes(); // skip ref count bytes reader.skip(sizeof(size_t)); size_t casesBeginOffset = layoutStrOffset + reader.offset + (numCases * sizeof(size_t)); for (size_t j = 0; j < numCases; j++) { size_t caseOffset = reader.readBytes(); const uint8_t *caseLayoutString = fieldLayoutStr + reader.offset + (numCases * sizeof(size_t)) + caseOffset; swift_resolve_resilientAccessors(layoutStr, casesBeginOffset + caseOffset, caseLayoutString, fieldType); } break; } case RefCountingKind::MultiPayloadEnumFNResolved: { // skip function pointer reader.skip(sizeof(uintptr_t)); size_t numCases = reader.readBytes(); size_t refCountBytes = reader.readBytes(); // skip enum size, offsets and ref counts reader.skip(sizeof(size_t) + (numCases * sizeof(size_t)) + refCountBytes); break; } case RefCountingKind::MultiPayloadEnumGeneric: { reader.skip(sizeof(size_t)); auto numPayloads = reader.readBytes(); auto refCountBytes = reader.readBytes(); reader.skip(sizeof(size_t) * (numPayloads + 1) + refCountBytes); break; } default: break; } } } extern "C" void swift_generic_instantiateLayoutString(const uint8_t* layoutStr, Metadata* type) { type->setLayoutString(layoutStr); }