mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
- Create the value witness table as a separate global object instead of concatenating it to the metadata pattern. - Always pass the metadata to the runtime and let the runtime handle instantiating or modifying the value witness table. - Pass the right layout algorithm version to the runtime; currently this is always "Swift 5". - Create a runtime function to instantiate single-case enums. Among other things, this makes the copying of the VWT, and any modifications of it, explicit and in the runtime, which is more future-proof.
346 lines
13 KiB
C++
346 lines
13 KiB
C++
//===--- Enum.cpp - Runtime declarations for enums ------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Swift runtime functions in support of enums.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "swift/Runtime/Metadata.h"
|
|
#include "swift/Runtime/Enum.h"
|
|
#include "swift/Runtime/Debug.h"
|
|
#include "Private.h"
|
|
#include "EnumImpl.h"
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
using namespace swift;
|
|
|
|
static EnumValueWitnessTable *getMutableVWTableForInit(EnumMetadata *self,
|
|
EnumLayoutFlags flags) {
|
|
auto oldTable =
|
|
static_cast<const EnumValueWitnessTable *>(self->getValueWitnesses());
|
|
|
|
// If we can alter the existing table in-place, do so.
|
|
if (isValueWitnessTableMutable(flags))
|
|
return const_cast<EnumValueWitnessTable*>(oldTable);
|
|
|
|
// Otherwise, allocate permanent memory for it and copy the existing table.
|
|
void *memory = allocateMetadata(sizeof(EnumValueWitnessTable),
|
|
alignof(EnumValueWitnessTable));
|
|
auto newTable = new (memory) EnumValueWitnessTable(*oldTable);
|
|
self->setValueWitnesses(newTable);
|
|
|
|
return newTable;
|
|
}
|
|
|
|
void
|
|
swift::swift_initEnumMetadataSingleCase(EnumMetadata *self,
|
|
EnumLayoutFlags layoutFlags,
|
|
const TypeLayout *payloadLayout) {
|
|
auto vwtable = getMutableVWTableForInit(self, layoutFlags);
|
|
|
|
vwtable->size = payloadLayout->size;
|
|
vwtable->stride = payloadLayout->stride;
|
|
vwtable->flags = payloadLayout->flags.withEnumWitnesses(true);
|
|
|
|
if (payloadLayout->flags.hasExtraInhabitants()) {
|
|
auto ew = static_cast<ExtraInhabitantsValueWitnessTable*>(vwtable);
|
|
ew->extraInhabitantFlags = payloadLayout->getExtraInhabitantFlags();
|
|
}
|
|
}
|
|
|
|
void
|
|
swift::swift_initEnumMetadataSinglePayload(EnumMetadata *self,
|
|
EnumLayoutFlags layoutFlags,
|
|
const TypeLayout *payloadLayout,
|
|
unsigned emptyCases) {
|
|
size_t payloadSize = payloadLayout->size;
|
|
unsigned payloadNumExtraInhabitants
|
|
= payloadLayout->getNumExtraInhabitants();
|
|
|
|
unsigned unusedExtraInhabitants = 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 {
|
|
size = payloadSize + getNumTagBytes(payloadSize,
|
|
emptyCases - payloadNumExtraInhabitants,
|
|
1 /*payload case*/);
|
|
}
|
|
|
|
auto vwtable = getMutableVWTableForInit(self, layoutFlags);
|
|
|
|
size_t align = payloadLayout->flags.getAlignment();
|
|
vwtable->size = size;
|
|
vwtable->flags = payloadLayout->flags
|
|
.withExtraInhabitants(unusedExtraInhabitants > 0)
|
|
.withEnumWitnesses(true)
|
|
.withInlineStorage(ValueWitnessTable::isValueInline(size, align));
|
|
auto rawStride = llvm::alignTo(size, align);
|
|
vwtable->stride = rawStride == 0 ? 1 : rawStride;
|
|
|
|
// 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 UnknownObject. 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(vwtable);
|
|
#if OPTIONAL_OBJECT_OPTIMIZATION
|
|
}
|
|
#endif
|
|
|
|
|
|
// If the payload has extra inhabitants left over after the ones we used,
|
|
// forward them as our own.
|
|
if (unusedExtraInhabitants > 0) {
|
|
auto xiVWTable = static_cast<ExtraInhabitantsValueWitnessTable*>(vwtable);
|
|
xiVWTable->extraInhabitantFlags = ExtraInhabitantFlags()
|
|
.withNumExtraInhabitants(unusedExtraInhabitants);
|
|
}
|
|
}
|
|
|
|
int swift::swift_getEnumCaseSinglePayload(const OpaqueValue *value,
|
|
const Metadata *payload,
|
|
unsigned emptyCases)
|
|
SWIFT_CC(RegisterPreservingCC_IMPL) {
|
|
|
|
auto *payloadWitnesses = payload->getValueWitnesses();
|
|
auto size = payloadWitnesses->getSize();
|
|
auto numExtraInhabitants = payloadWitnesses->getNumExtraInhabitants();
|
|
auto getExtraInhabitantIndex =
|
|
(static_cast<const ExtraInhabitantsValueWitnessTable *>(payloadWitnesses)
|
|
->getExtraInhabitantIndex);
|
|
|
|
return getEnumTagSinglePayloadImpl(value, emptyCases, payload, size,
|
|
numExtraInhabitants,
|
|
getExtraInhabitantIndex);
|
|
}
|
|
|
|
void swift::swift_storeEnumTagSinglePayload(OpaqueValue *value,
|
|
const Metadata *payload,
|
|
int whichCase, unsigned emptyCases)
|
|
SWIFT_CC(RegisterPreservingCC_IMPL) {
|
|
|
|
auto *payloadWitnesses = payload->getValueWitnesses();
|
|
auto size = payloadWitnesses->getSize();
|
|
auto numExtraInhabitants = payloadWitnesses->getNumExtraInhabitants();
|
|
auto storeExtraInhabitant =
|
|
(static_cast<const ExtraInhabitantsValueWitnessTable *>(payloadWitnesses)
|
|
->storeExtraInhabitant);
|
|
|
|
storeEnumTagSinglePayloadImpl(value, whichCase, emptyCases, payload, size,
|
|
numExtraInhabitants, storeExtraInhabitant);
|
|
}
|
|
|
|
void
|
|
swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType,
|
|
EnumLayoutFlags layoutFlags,
|
|
unsigned numPayloads,
|
|
const TypeLayout * const *payloadLayouts) {
|
|
// Accumulate the layout requirements of the payloads.
|
|
size_t payloadSize = 0, alignMask = 0;
|
|
bool isPOD = true, isBT = true;
|
|
for (unsigned i = 0; i < numPayloads; ++i) {
|
|
const TypeLayout *payloadLayout = payloadLayouts[i];
|
|
payloadSize
|
|
= std::max(payloadSize, (size_t)payloadLayout->size);
|
|
alignMask |= payloadLayout->flags.getAlignmentMask();
|
|
isPOD &= payloadLayout->flags.isPOD();
|
|
isBT &= payloadLayout->flags.isBitwiseTakable();
|
|
}
|
|
|
|
// Store the max payload size in the metadata.
|
|
assignUnlessEqual(enumType->getPayloadSize(), payloadSize);
|
|
|
|
// The total size includes space for the tag.
|
|
unsigned totalSize = payloadSize + getNumTagBytes(payloadSize,
|
|
enumType->Description->Enum.getNumEmptyCases(),
|
|
numPayloads);
|
|
|
|
auto vwtable = getMutableVWTableForInit(enumType, layoutFlags);
|
|
|
|
// Set up the layout info in the vwtable.
|
|
vwtable->size = totalSize;
|
|
vwtable->flags = ValueWitnessFlags()
|
|
.withAlignmentMask(alignMask)
|
|
.withPOD(isPOD)
|
|
.withBitwiseTakable(isBT)
|
|
// TODO: Extra inhabitants
|
|
.withExtraInhabitants(false)
|
|
.withEnumWitnesses(true)
|
|
.withInlineStorage(ValueWitnessTable::isValueInline(totalSize, alignMask+1))
|
|
;
|
|
auto rawStride = (totalSize + alignMask) & ~alignMask;
|
|
vwtable->stride = rawStride == 0 ? 1 : rawStride;
|
|
|
|
installCommonValueWitnesses(vwtable);
|
|
}
|
|
|
|
namespace {
|
|
struct MultiPayloadLayout {
|
|
size_t payloadSize;
|
|
size_t numTagBytes;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static MultiPayloadLayout getMultiPayloadLayout(const EnumMetadata *enumType) {
|
|
size_t payloadSize = enumType->getPayloadSize();
|
|
size_t totalSize = enumType->getValueWitnesses()->size;
|
|
return {payloadSize, totalSize - payloadSize};
|
|
}
|
|
|
|
static void storeMultiPayloadTag(OpaqueValue *value,
|
|
MultiPayloadLayout layout,
|
|
unsigned tag) {
|
|
auto tagBytes = reinterpret_cast<char *>(value) + layout.payloadSize;
|
|
#if defined(__BIG_ENDIAN__)
|
|
small_memcpy(tagBytes,
|
|
reinterpret_cast<char *>(&tag) + 4 - layout.numTagBytes,
|
|
layout.numTagBytes);
|
|
#else
|
|
small_memcpy(tagBytes, &tag, layout.numTagBytes);
|
|
#endif
|
|
}
|
|
|
|
static void storeMultiPayloadValue(OpaqueValue *value,
|
|
MultiPayloadLayout layout,
|
|
unsigned payloadValue) {
|
|
auto bytes = reinterpret_cast<char *>(value);
|
|
#if defined(__BIG_ENDIAN__)
|
|
unsigned numPayloadValueBytes =
|
|
std::min(layout.payloadSize, sizeof(payloadValue));
|
|
memcpy(bytes + sizeof(OpaqueValue *) - numPayloadValueBytes,
|
|
reinterpret_cast<char *>(&payloadValue) + 4 - numPayloadValueBytes,
|
|
numPayloadValueBytes);
|
|
if (layout.payloadSize > sizeof(payloadValue) &&
|
|
layout.payloadSize > sizeof(OpaqueValue *)) {
|
|
memset(bytes, 0,
|
|
sizeof(OpaqueValue *) - numPayloadValueBytes);
|
|
memset(bytes + sizeof(OpaqueValue *), 0,
|
|
layout.payloadSize - sizeof(OpaqueValue *));
|
|
}
|
|
#else
|
|
memcpy(bytes, &payloadValue,
|
|
std::min(layout.payloadSize, sizeof(payloadValue)));
|
|
|
|
// If the payload is larger than the value, zero out the rest.
|
|
if (layout.payloadSize > sizeof(payloadValue))
|
|
memset(bytes + sizeof(payloadValue), 0,
|
|
layout.payloadSize - sizeof(payloadValue));
|
|
#endif
|
|
}
|
|
|
|
static unsigned loadMultiPayloadTag(const OpaqueValue *value,
|
|
MultiPayloadLayout layout) {
|
|
auto tagBytes = reinterpret_cast<const char *>(value) + layout.payloadSize;
|
|
|
|
unsigned tag = 0;
|
|
#if defined(__BIG_ENDIAN__)
|
|
small_memcpy(reinterpret_cast<char *>(&tag) + 4 - layout.numTagBytes,
|
|
tagBytes, layout.numTagBytes);
|
|
#else
|
|
small_memcpy(&tag, tagBytes, layout.numTagBytes);
|
|
#endif
|
|
|
|
return tag;
|
|
}
|
|
|
|
static unsigned loadMultiPayloadValue(const OpaqueValue *value,
|
|
MultiPayloadLayout layout) {
|
|
auto bytes = reinterpret_cast<const char *>(value);
|
|
unsigned payloadValue = 0;
|
|
#if defined(__BIG_ENDIAN__)
|
|
unsigned numPayloadValueBytes =
|
|
std::min(layout.payloadSize, sizeof(payloadValue));
|
|
memcpy(reinterpret_cast<char *>(&payloadValue) + 4 - numPayloadValueBytes,
|
|
bytes + sizeof(OpaqueValue *) - numPayloadValueBytes, numPayloadValueBytes);
|
|
#else
|
|
memcpy(&payloadValue, bytes,
|
|
std::min(layout.payloadSize, sizeof(payloadValue)));
|
|
#endif
|
|
return payloadValue;
|
|
}
|
|
|
|
void
|
|
swift::swift_storeEnumTagMultiPayload(OpaqueValue *value,
|
|
const EnumMetadata *enumType,
|
|
unsigned whichCase) {
|
|
auto layout = getMultiPayloadLayout(enumType);
|
|
unsigned numPayloads = enumType->Description->Enum.getNumPayloadCases();
|
|
if (whichCase < numPayloads) {
|
|
// For a payload case, store the tag after the payload area.
|
|
storeMultiPayloadTag(value, layout, whichCase);
|
|
} else {
|
|
// For an empty case, factor out the parts that go in the payload and
|
|
// tag areas.
|
|
unsigned whichEmptyCase = whichCase - numPayloads;
|
|
unsigned whichTag, whichPayloadValue;
|
|
if (layout.payloadSize >= 4) {
|
|
whichTag = numPayloads;
|
|
whichPayloadValue = whichEmptyCase;
|
|
} else {
|
|
unsigned numPayloadBits = layout.payloadSize * CHAR_BIT;
|
|
whichTag = numPayloads + (whichEmptyCase >> numPayloadBits);
|
|
whichPayloadValue = whichEmptyCase & ((1U << numPayloads) - 1U);
|
|
}
|
|
storeMultiPayloadTag(value, layout, whichTag);
|
|
storeMultiPayloadValue(value, layout, whichPayloadValue);
|
|
}
|
|
}
|
|
|
|
unsigned
|
|
swift::swift_getEnumCaseMultiPayload(const OpaqueValue *value,
|
|
const EnumMetadata *enumType) {
|
|
auto layout = getMultiPayloadLayout(enumType);
|
|
unsigned numPayloads = enumType->Description->Enum.getNumPayloadCases();
|
|
|
|
unsigned tag = loadMultiPayloadTag(value, layout);
|
|
if (tag < numPayloads) {
|
|
// If the tag indicates a payload, then we're done.
|
|
return tag;
|
|
} else {
|
|
// Otherwise, the other part of the discriminator is in the payload.
|
|
unsigned payloadValue = loadMultiPayloadValue(value, layout);
|
|
|
|
if (layout.payloadSize >= 4) {
|
|
return numPayloads + payloadValue;
|
|
} else {
|
|
unsigned numPayloadBits = layout.payloadSize * CHAR_BIT;
|
|
return (payloadValue | (tag - numPayloads) << numPayloadBits)
|
|
+ numPayloads;
|
|
}
|
|
}
|
|
}
|