mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
214 lines
7.5 KiB
C++
214 lines
7.5 KiB
C++
//===--- EnumImpl.h - Enum implementation runtime declarations --*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Enum implementation details declarations of the Swift runtime.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#ifndef SWIFT_RUNTIME_ENUMIMPL_H
|
|
#define SWIFT_RUNTIME_ENUMIMPL_H
|
|
|
|
namespace swift {
|
|
|
|
/// This is a small and fast implementation of memcpy with a constant count. It
|
|
/// should be a performance win for small constant values where the function
|
|
/// can be inlined, the loop unrolled and the memory accesses merged.
|
|
template <unsigned count> static void small_memcpy(void *dest, const void *src) {
|
|
uint8_t *d8 = (uint8_t*)dest;
|
|
const uint8_t *s8 = (const uint8_t*)src;
|
|
for (unsigned i = 0; i < count; i++) {
|
|
*d8++ = *s8++;
|
|
}
|
|
}
|
|
|
|
static inline void small_memcpy(void *dest, const void *src, unsigned count,
|
|
bool countMaybeThree = false) {
|
|
// This is specialization of the memcpy line below with
|
|
// specialization for values of 1, 2 and 4.
|
|
// memcpy(dst, src, count)
|
|
if (count == 1) {
|
|
small_memcpy<1>(dest, src);
|
|
} else if (count == 2) {
|
|
small_memcpy<2>(dest, src);
|
|
} else if (countMaybeThree && count == 3) {
|
|
small_memcpy<3>(dest, src);
|
|
} else if (count == 4) {
|
|
small_memcpy<4>(dest, src);
|
|
} else {
|
|
swift::crash("Tagbyte values should be 1, 2 or 4.");
|
|
}
|
|
}
|
|
|
|
static inline void small_memset(void *dest, uint8_t value, unsigned count) {
|
|
if (count == 1) {
|
|
memset(dest, value, 1);
|
|
} else if (count == 2) {
|
|
memset(dest, value, 2);
|
|
} else if (count == 4) {
|
|
memset(dest, value, 4);
|
|
} else {
|
|
swift::crash("Tagbyte values should be 1, 2 or 4.");
|
|
}
|
|
}
|
|
|
|
inline unsigned getNumTagBytes(size_t size, unsigned emptyCases,
|
|
unsigned payloadCases) {
|
|
// We can use the payload area with a tag bit set somewhere outside of the
|
|
// payload area to represent cases. See how many bytes we need to cover
|
|
// all the empty cases.
|
|
|
|
unsigned numTags = payloadCases;
|
|
if (emptyCases > 0) {
|
|
if (size >= 4)
|
|
// Assume that one tag bit is enough if the precise calculation overflows
|
|
// an int32.
|
|
numTags += 1;
|
|
else {
|
|
unsigned bits = size * 8U;
|
|
unsigned casesPerTagBitValue = 1U << bits;
|
|
numTags += ((emptyCases + (casesPerTagBitValue-1U)) >> bits);
|
|
}
|
|
}
|
|
return (numTags <= 1 ? 0 :
|
|
numTags < 256 ? 1 :
|
|
numTags < 65536 ? 2 : 4);
|
|
}
|
|
|
|
inline unsigned getEnumTagSinglePayloadImpl(
|
|
const OpaqueValue *enumAddr, unsigned emptyCases, const Metadata *payload,
|
|
size_t payloadSize, size_t payloadNumExtraInhabitants,
|
|
int (*getExtraInhabitantIndex)(const OpaqueValue *, const Metadata *)) {
|
|
|
|
// If there are extra tag bits, check them.
|
|
if (emptyCases > payloadNumExtraInhabitants) {
|
|
auto *valueAddr = reinterpret_cast<const uint8_t *>(enumAddr);
|
|
auto *extraTagBitAddr = valueAddr + payloadSize;
|
|
unsigned extraTagBits = 0;
|
|
unsigned numBytes =
|
|
getNumTagBytes(payloadSize, emptyCases - payloadNumExtraInhabitants,
|
|
1 /*payload case*/);
|
|
|
|
#if defined(__BIG_ENDIAN__)
|
|
small_memcpy(reinterpret_cast<uint8_t *>(&extraTagBits) + 4 - numBytes,
|
|
extraTagBitAddr, numBytes);
|
|
#else
|
|
small_memcpy(&extraTagBits, extraTagBitAddr, numBytes);
|
|
#endif
|
|
|
|
// If the extra tag bits are zero, we have a valid payload or
|
|
// extra inhabitant (checked below). If nonzero, form the case index from
|
|
// the extra tag value and the value stored in the payload.
|
|
if (extraTagBits > 0) {
|
|
unsigned caseIndexFromExtraTagBits =
|
|
payloadSize >= 4 ? 0 : (extraTagBits - 1U) << (payloadSize * 8U);
|
|
|
|
// In practice we should need no more than four bytes from the payload
|
|
// area.
|
|
unsigned caseIndexFromValue = 0;
|
|
unsigned numPayloadTagBytes = std::min(size_t(4), payloadSize);
|
|
#if defined(__BIG_ENDIAN__)
|
|
if (numPayloadTagBytes)
|
|
small_memcpy(reinterpret_cast<uint8_t *>(&caseIndexFromValue) + 4 -
|
|
numPayloadTagBytes,
|
|
valueAddr, numPayloadTagBytes, true);
|
|
#else
|
|
if (numPayloadTagBytes)
|
|
small_memcpy(&caseIndexFromValue, valueAddr,
|
|
numPayloadTagBytes, true);
|
|
#endif
|
|
unsigned noPayloadIndex =
|
|
(caseIndexFromExtraTagBits | caseIndexFromValue) +
|
|
payloadNumExtraInhabitants;
|
|
return noPayloadIndex + 1;
|
|
}
|
|
}
|
|
|
|
// If there are extra inhabitants, see whether the payload is valid.
|
|
if (payloadNumExtraInhabitants > 0) {
|
|
return getExtraInhabitantIndex(enumAddr, payload) + 1;
|
|
}
|
|
|
|
// Otherwise, we have always have a valid payload.
|
|
return 0;
|
|
}
|
|
|
|
inline void storeEnumTagSinglePayloadImpl(
|
|
OpaqueValue *value, unsigned whichCase, unsigned emptyCases,
|
|
const Metadata *payload, size_t payloadSize,
|
|
size_t payloadNumExtraInhabitants,
|
|
void (*storeExtraInhabitant)(OpaqueValue *, int whichCase,
|
|
const Metadata *)) {
|
|
|
|
auto *valueAddr = reinterpret_cast<uint8_t *>(value);
|
|
auto *extraTagBitAddr = valueAddr + payloadSize;
|
|
unsigned numExtraTagBytes =
|
|
emptyCases > payloadNumExtraInhabitants
|
|
? getNumTagBytes(payloadSize, emptyCases - payloadNumExtraInhabitants,
|
|
1 /*payload case*/)
|
|
: 0;
|
|
|
|
// For payload or extra inhabitant cases, zero-initialize the extra tag bits,
|
|
// if any.
|
|
if (whichCase <= payloadNumExtraInhabitants) {
|
|
if (numExtraTagBytes != 0)
|
|
small_memset(extraTagBitAddr, 0, numExtraTagBytes);
|
|
|
|
// If this is the payload case, we're done.
|
|
if (whichCase == 0)
|
|
return;
|
|
|
|
// Store the extra inhabitant.
|
|
unsigned noPayloadIndex = whichCase - 1;
|
|
storeExtraInhabitant(value, noPayloadIndex, payload);
|
|
return;
|
|
}
|
|
|
|
// Factor the case index into payload and extra tag parts.
|
|
unsigned noPayloadIndex = whichCase - 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 defined(__BIG_ENDIAN__)
|
|
unsigned numPayloadTagBytes = std::min(size_t(4), payloadSize);
|
|
if (numPayloadTagBytes)
|
|
small_memcpy(valueAddr,
|
|
reinterpret_cast<uint8_t *>(&payloadIndex) + 4 -
|
|
numPayloadTagBytes,
|
|
numPayloadTagBytes, true);
|
|
if (numExtraTagBytes)
|
|
small_memcpy(extraTagBitAddr,
|
|
reinterpret_cast<uint8_t *>(&extraTagIndex) + 4 -
|
|
numExtraTagBytes,
|
|
numExtraTagBytes);
|
|
#else
|
|
unsigned numPayloadTagBytes = std::min(size_t(4), payloadSize);
|
|
if (numPayloadTagBytes)
|
|
small_memcpy(valueAddr, &payloadIndex, numPayloadTagBytes, true);
|
|
if (payloadSize > 4)
|
|
memset(valueAddr + 4, 0, payloadSize - 4);
|
|
if (numExtraTagBytes)
|
|
small_memcpy(extraTagBitAddr, &extraTagIndex, numExtraTagBytes);
|
|
#endif
|
|
}
|
|
|
|
} /* end namespace swift */
|
|
|
|
#endif /* SWIFT_RUNTIME_ENUMIMPL_H */
|