[IRGen] Handle complex single payload enum cases (#66289)

* [IRGen] Handle complex single payload enum cases

rdar://110138498

Handles single payload enum cases with more complex bit patterns (e.g. >64 bits or scattered) by storing a relative pointer to a function that reads the tag.

* Use proper symbol for enum tag helper
This commit is contained in:
Dario Rexin
2023-06-02 11:49:57 -07:00
committed by GitHub
parent a5f0dbcff6
commit 5cbf9f9641
8 changed files with 299 additions and 18 deletions

View File

@@ -400,6 +400,16 @@ std::string IRGenMangler::mangleSymbolNameForMangledConformanceAccessorString(
return finalize();
}
std::string IRGenMangler::mangleSymbolNameForMangledGetEnumTagForLayoutString(
CanType type) {
beginManglingWithoutPrefix();
Buffer << "get_enum_tag_for_layout_string"
<< " ";
appendType(type, nullptr);
return finalize();
}
std::string IRGenMangler::mangleSymbolNameForUnderlyingTypeAccessorString(
OpaqueTypeDecl *opaque, unsigned index) {
beginManglingWithoutPrefix();

View File

@@ -633,6 +633,8 @@ public:
CanType type,
ProtocolConformanceRef conformance);
std::string mangleSymbolNameForMangledGetEnumTagForLayoutString(CanType type);
std::string
mangleSymbolNameForUnderlyingTypeAccessorString(OpaqueTypeDecl *opaque,
unsigned index);

View File

@@ -76,6 +76,8 @@ public:
Existential = 0x0e,
Resilient = 0x0f,
SinglePayloadEnumSimple = 0x10,
SinglePayloadEnumFN = 0x11,
SinglePayloadEnumFNResolved = 0x12,
Skip = 0x80,
// We may use the MSB as flag that a count follows,
@@ -93,12 +95,19 @@ private:
const TypeLayoutEntry *payload;
};
struct SinglePayloadEnumFN {
llvm::Function *tagFn;
unsigned extraTagByteCount;
const TypeLayoutEntry *payload;
};
struct RefCounting {
RefCountingKind kind;
union {
size_t size;
llvm::Function* metaTypeRef;
SinglePayloadEnumSimple singlePayloadEnumSimple;
SinglePayloadEnumFN singlePayloadEnumFN;
};
RefCounting() = default;
@@ -132,6 +141,16 @@ public:
refCountings.push_back(op);
}
void addSinglePayloadEnumFN(llvm::Function *tagFn, unsigned extraTagByteCount,
const TypeLayoutEntry *payload) {
RefCounting op;
op.kind = RefCountingKind::SinglePayloadEnumFN;
op.singlePayloadEnumFN.tagFn = tagFn;
op.singlePayloadEnumSimple.extraTagByteCount = extraTagByteCount;
op.singlePayloadEnumFN.payload = payload;
refCountings.push_back(op);
}
void addSkip(size_t size) {
if (refCountings.empty() ||
refCountings.back().kind != RefCountingKind::Skip) {
@@ -227,6 +246,40 @@ public:
break;
}
case RefCountingKind::SinglePayloadEnumFN: {
uint64_t op = (static_cast<uint64_t>(refCounting.kind) << 56) | skip;
B.addInt64(op);
skip = 0;
size_t nestedRefCountBytes = 0;
auto enumData = refCounting.singlePayloadEnumFN;
B.addRelativeOffset(IGM.IntPtrTy, enumData.tagFn);
auto nestedRefCountBytesPlaceholder =
B.addPlaceholderWithSize(IGM.SizeTy);
auto skipBytesPlaceholder = B.addPlaceholderWithSize(IGM.SizeTy);
LayoutStringBuilder nestedBuilder{};
enumData.payload->refCountString(IGM, nestedBuilder, genericSig);
addRefCountings(IGM, B, genericSig, nestedBuilder.refCountings, skip,
nestedRefCountBytes, flags);
auto nestedSkip = enumData.payload->fixedSize(IGM)->getValue() - skip;
B.fillPlaceholderWithInt(nestedRefCountBytesPlaceholder, IGM.SizeTy,
nestedRefCountBytes);
B.fillPlaceholderWithInt(skipBytesPlaceholder, IGM.SizeTy, nestedSkip);
refCountBytes += (sizeof(uint64_t)) +
(3 * IGM.getPointerSize().getValue()) +
nestedRefCountBytes;
skip += enumData.extraTagByteCount;
flags |= LayoutStringFlags::HasRelativePointers;
break;
}
default: {
uint64_t op = (static_cast<uint64_t>(refCounting.kind) << 56) | skip;
B.addInt64(op);
@@ -362,6 +415,30 @@ llvm::Function *createMetatypeAccessorFunction(IRGenModule &IGM, SILType ty,
return static_cast<llvm::Function *>(helperFn);
}
llvm::Function *createFixedEnumLoadTag(IRGenModule &IGM,
const EnumTypeLayoutEntry &entry) {
assert(entry.isFixedSize(IGM));
IRGenMangler mangler;
auto symbol = mangler.mangleSymbolNameForMangledGetEnumTagForLayoutString(
entry.ty.getASTType());
auto helperFn = IGM.getOrCreateHelperFunction(
symbol, IGM.Int32Ty /*retTy*/, IGM.Int8PtrTy /*argTys*/,
[&](IRGenFunction &IGF) {
auto enumPtr = IGF.collectParameters().claimNext();
auto *typeInfo = *entry.fixedTypeInfo;
auto enumType = typeInfo->getStorageType()->getPointerTo();
auto castEnumPtr = IGF.Builder.CreateBitCast(enumPtr, enumType);
auto enumAddr = typeInfo->getAddressForPointer(castEnumPtr);
auto tag = entry.getEnumTag(IGF, enumAddr);
IGF.Builder.CreateRet(tag);
});
return static_cast<llvm::Function *>(helperFn);
}
TypeLayoutEntry::~TypeLayoutEntry() {}
void TypeLayoutEntry::computeProperties() {
@@ -2069,6 +2146,7 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString(
uint64_t zeroTagValue = 0;
unsigned xiBitCount = 0;
unsigned xiOffset = 0;
bool isSimple = true;
auto &payloadTI = **cases[0]->getFixedTypeInfo();
@@ -2088,12 +2166,12 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString(
(tzCount % toCount != 0)) {
// We currently don't handle cases with non-contiguous or > 64 bits of
// extra inhabitants
return false;
isSimple = false;
} else {
xiBitCount = std::min(64u, mask.countPopulation());
xiOffset = mask.countTrailingZeros();
zeroTagValue = lowValue.extractBitsAsZExtValue(xiBitCount, xiOffset);
}
xiBitCount = std::min(64u, mask.countPopulation());
xiOffset = mask.countTrailingZeros();
zeroTagValue = lowValue.extractBitsAsZExtValue(xiBitCount, xiOffset);
}
}
@@ -2119,8 +2197,13 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString(
return false;
}
B.addSinglePayloadEnumSimple(zeroTagValue, xiTagValues, extraTagByteCount,
xiBitCount / 8, xiOffset, cases[0]);
if (isSimple) {
B.addSinglePayloadEnumSimple(zeroTagValue, xiTagValues, extraTagByteCount,
xiBitCount / 8, xiOffset, cases[0]);
} else {
auto tagFn = createFixedEnumLoadTag(IGM, *this);
B.addSinglePayloadEnumFN(tagFn, extraTagByteCount, cases[0]);
}
return true;
}

View File

@@ -26,6 +26,7 @@
#include "llvm/Support/SwapByteOrder.h"
#include <cstdint>
#include <limits>
#include <type_traits>
#if SWIFT_OBJC_INTEROP
#include "swift/Runtime/ObjCBridge.h"
#include <Block.h>
@@ -60,24 +61,33 @@ static Metadata *getExistentialTypeMetadata(OpaqueValue *object) {
return reinterpret_cast<Metadata**>(object)[NumWords_ValueBuffer];
}
typedef Metadata* (*MetadataAccessor)(const Metadata* const *);
template <typename FnTy>
static const FnTy readRelativeFunctionPointer(const uint8_t *layoutStr,
size_t &offset) {
static_assert(std::is_pointer<FnTy>::value);
static const Metadata *getResilientTypeMetadata(const Metadata* metadata,
const uint8_t *layoutStr,
size_t &offset) {
auto absolute = layoutStr + offset;
auto relativeOffset =
(uintptr_t)(intptr_t)(int32_t)readBytes<intptr_t>(layoutStr, offset);
MetadataAccessor fn;
FnTy fn;
#if SWIFT_PTRAUTH
fn = (MetadataAccessor)ptrauth_sign_unauthenticated(
fn = (FnTy)ptrauth_sign_unauthenticated(
(void *)((uintptr_t)absolute + relativeOffset),
ptrauth_key_function_pointer, 0);
#else
fn = (MetadataAccessor)((uintptr_t)absolute + relativeOffset);
fn = (FnTy)((uintptr_t)absolute + relativeOffset);
#endif
return fn;
}
typedef Metadata *(*MetadataAccessor)(const Metadata *const *);
static const Metadata *getResilientTypeMetadata(const Metadata *metadata,
const uint8_t *layoutStr,
size_t &offset) {
auto fn = readRelativeFunctionPointer<MetadataAccessor>(layoutStr, offset);
return fn(metadata->getGenericArgs());
}
@@ -117,6 +127,13 @@ inline static bool handleNextRefCount(const Metadata *metadata, const uint8_t *t
} else if (SWIFT_UNLIKELY(tag ==
RefCountingKind::SinglePayloadEnumSimple)) {
Handler::handleSinglePayloadEnumSimple(typeLayout, offset, addrOffset, std::forward<Params>(params)...);
} else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumFN)) {
Handler::handleSinglePayloadEnumFN(typeLayout, offset, false, addrOffset,
std::forward<Params>(params)...);
} else if (SWIFT_UNLIKELY(tag ==
RefCountingKind::SinglePayloadEnumFNResolved)) {
Handler::handleSinglePayloadEnumFN(typeLayout, offset, true, addrOffset,
std::forward<Params>(params)...);
} else {
Handler::handleReference(tag, addrOffset, std::forward<Params>(params)...);
}
@@ -154,8 +171,9 @@ static uint64_t readTagBytes(uint8_t *addr, uint8_t byteCount) {
}
}
static void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, size_t &offset,
uint8_t *addr, size_t &addrOffset) {
static void handleSinglePayloadEnumSimple(const uint8_t *typeLayout,
size_t &offset, uint8_t *addr,
size_t &addrOffset) {
auto byteCountsAndOffset = readBytes<uint64_t>(typeLayout, offset);
auto extraTagBytesPattern = (uint8_t)(byteCountsAndOffset >> 62);
auto xiTagBytesPattern = ((uint8_t)(byteCountsAndOffset >> 59)) & 0x7;
@@ -198,6 +216,30 @@ noPayload:
addrOffset += skip;
}
typedef unsigned (*GetEnumTagFn)(const uint8_t *);
static void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset,
bool resolved, uint8_t *addr,
size_t &addrOffset) {
GetEnumTagFn getEnumTag;
if (resolved) {
getEnumTag = readBytes<GetEnumTagFn>(typeLayout, offset);
} else {
getEnumTag = readRelativeFunctionPointer<GetEnumTagFn>(typeLayout, offset);
}
unsigned enumTag = getEnumTag(addr + addrOffset);
if (enumTag == 0) {
offset += sizeof(size_t) * 2;
} else {
auto refCountBytes = readBytes<size_t>(typeLayout, offset);
auto skip = readBytes<size_t>(typeLayout, offset);
offset += refCountBytes;
addrOffset += skip;
}
}
const DestroyFuncAndMask destroyTable[] = {
{(DestrFn)&skipDestroy, false},
{(DestrFn)&swift_errorRelease, true},
@@ -232,6 +274,13 @@ struct DestroyHandler {
::handleSinglePayloadEnumSimple(typeLayout, offset, addr, addrOffset);
}
static inline void handleSinglePayloadEnumFN(const uint8_t *typeLayout,
size_t &offset, bool resolved,
size_t &addrOffset,
uint8_t *addr) {
::handleSinglePayloadEnumFN(typeLayout, offset, resolved, addr, addrOffset);
}
static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *addr) {
const auto &destroyFunc = destroyTable[static_cast<uint8_t>(tag)];
if (SWIFT_LIKELY(destroyFunc.isIndirect)) {
@@ -305,6 +354,14 @@ struct CopyHandler {
::handleSinglePayloadEnumSimple(typeLayout, offset, (uint8_t *)src, addrOffset);
}
static inline void handleSinglePayloadEnumFN(const uint8_t *typeLayout,
size_t &offset, bool resolved,
size_t &addrOffset,
uint8_t *dest, uint8_t *src) {
::handleSinglePayloadEnumFN(typeLayout, offset, resolved, (uint8_t *)src,
addrOffset);
}
static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) {
const auto &retainFunc = retainTable[static_cast<uint8_t>(tag)];
if (SWIFT_LIKELY(retainFunc.isSingle)) {
@@ -388,6 +445,18 @@ swift_generic_initWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src,
break;
}
case RefCountingKind::SinglePayloadEnumFN: {
handleSinglePayloadEnumFN(typeLayout, offset, false, (uint8_t *)src,
addrOffset);
break;
}
case RefCountingKind::SinglePayloadEnumFNResolved: {
handleSinglePayloadEnumFN(typeLayout, offset, true, (uint8_t *)src,
addrOffset);
break;
}
case RefCountingKind::End:
return dest;
default:
@@ -439,6 +508,25 @@ void swift::swift_resolve_resilientAccessors(
case RefCountingKind::SinglePayloadEnumSimple:
i += (3 * sizeof(uint64_t)) + (4 * sizeof(size_t));
break;
case RefCountingKind::SinglePayloadEnumFN: {
auto getEnumTag =
readRelativeFunctionPointer<GetEnumTagFn>(fieldLayoutStr, i);
size_t writeOffset =
layoutStrOffset + currentOffset - layoutStringHeaderSize;
uint64_t tagAndOffset =
(((uint64_t)RefCountingKind::SinglePayloadEnumFNResolved) << 56) |
size;
writeBytes(layoutStr, writeOffset, tagAndOffset);
writeBytes(layoutStr, writeOffset, getEnumTag);
i += 2 * sizeof(size_t);
break;
}
case RefCountingKind::SinglePayloadEnumFNResolved:
i += 3 * sizeof(size_t);
break;
default:
break;
}

View File

@@ -44,6 +44,8 @@ enum class RefCountingKind : uint8_t {
Resilient = 0x0f,
SinglePayloadEnumSimple = 0x10,
SinglePayloadEnumFN = 0x11,
SinglePayloadEnumFNResolved = 0x12,
Skip = 0x80,
// We may use the MSB as flag that a count follows,

View File

@@ -442,6 +442,17 @@ public struct InternalEnumWrapper {
}
}
public enum SinglePayloadEnumManyXI {
case empty0
case empty1
case empty2
case empty3
case empty4
case empty5
case empty6
case nonEmpty(Builtin.Int127, SimpleClass)
}
public struct PrespecializedStruct<T> {
let y: Int = 0
let x: T

View File

@@ -1,10 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -prespecialize-generic-metadata -enable-experimental-feature LayoutStringValueWitnesses -enable-experimental-feature LayoutStringValueWitnessesInstantiation -enable-layout-string-value-witnesses -enable-layout-string-value-witnesses-instantiation -enable-type-layout -enable-autolinking-runtime-compatibility-bytecode-layouts -emit-module -emit-module-path=%t/layout_string_witnesses_types.swiftmodule %S/Inputs/layout_string_witnesses_types.swift
// RUN: %target-swift-frontend -prespecialize-generic-metadata -enable-experimental-feature LayoutStringValueWitnesses -enable-experimental-feature LayoutStringValueWitnessesInstantiation -enable-layout-string-value-witnesses -enable-layout-string-value-witnesses-instantiation -enable-type-layout -enable-autolinking-runtime-compatibility-bytecode-layouts -parse-stdlib -emit-module -emit-module-path=%t/layout_string_witnesses_types.swiftmodule %S/Inputs/layout_string_witnesses_types.swift
// RUN: %target-build-swift-dylib(%t/%target-library-name(layout_string_witnesses_types)) -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnesses -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnessesInstantiation -Xfrontend -enable-layout-string-value-witnesses -Xfrontend -enable-layout-string-value-witnesses-instantiation -Xfrontend -enable-type-layout -Xfrontend -parse-stdlib -parse-as-library %S/Inputs/layout_string_witnesses_types.swift
// RUN: %target-codesign %t/%target-library-name(layout_string_witnesses_types)
// RUN: %target-swift-frontend -enable-experimental-feature LayoutStringValueWitnesses -enable-experimental-feature LayoutStringValueWitnessesInstantiation -enable-layout-string-value-witnesses -enable-layout-string-value-witnesses-instantiation -enable-library-evolution -enable-autolinking-runtime-compatibility-bytecode-layouts -emit-module -emit-module-path=%t/layout_string_witnesses_types_resilient.swiftmodule %S/Inputs/layout_string_witnesses_types_resilient.swift
// RUN: %target-build-swift -g -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnesses -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnessesInstantiation -Xfrontend -enable-layout-string-value-witnesses -Xfrontend -enable-layout-string-value-witnesses-instantiation -Xfrontend -enable-library-evolution -c -parse-as-library -o %t/layout_string_witnesses_types_resilient.o %S/Inputs/layout_string_witnesses_types_resilient.swift
// RUN: %target-build-swift -g -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnesses -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnessesInstantiation -Xfrontend -enable-layout-string-value-witnesses -Xfrontend -enable-layout-string-value-witnesses-instantiation -Xfrontend -enable-type-layout -Xfrontend -enable-autolinking-runtime-compatibility-bytecode-layouts -module-name layout_string_witnesses_dynamic -llayout_string_witnesses_types -L%t %t/layout_string_witnesses_types_resilient.o -I %t -o %t/main %s %target-rpath(%t)
// RUN: %target-build-swift -g -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnesses -Xfrontend -enable-experimental-feature -Xfrontend LayoutStringValueWitnessesInstantiation -Xfrontend -enable-layout-string-value-witnesses -Xfrontend -enable-layout-string-value-witnesses-instantiation -Xfrontend -enable-type-layout -parse-stdlib -module-name layout_string_witnesses_dynamic -llayout_string_witnesses_types -L%t %t/layout_string_witnesses_types_resilient.o -I %t -o %t/main %s %target-rpath(%t)
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main %t/%target-library-name(layout_string_witnesses_types) | %FileCheck %s --check-prefix=CHECK -check-prefix=CHECK-%target-os
@@ -12,6 +12,7 @@
// UNSUPPORTED: back_deployment_runtime
import Swift
import layout_string_witnesses_types
import layout_string_witnesses_types_resilient
@@ -431,6 +432,34 @@ func testMixedEnumWrapperWrapperGeneric() {
testMixedEnumWrapperWrapperGeneric()
func testGenericSinglePayloadEnumManyXI() {
let ptr = allocateInternalGenericPtr(of: SinglePayloadEnumManyXI.self)
do {
let x = SinglePayloadEnumManyXI.nonEmpty(Builtin.zeroInitializer(), SimpleClass(x: 23))
testGenericInit(ptr, to: x)
}
do {
let y = SinglePayloadEnumManyXI.nonEmpty(Builtin.zeroInitializer(), SimpleClass(x: 23))
// 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: SinglePayloadEnumManyXI.self)
ptr.deallocate()
}
testGenericSinglePayloadEnumManyXI()
#if os(macOS)
import Foundation

View File

@@ -542,6 +542,62 @@ func testSinglePayloadEnumExtraTagBytesWrapper() {
testSinglePayloadEnumExtraTagBytesWrapper()
func testSinglePayloadEnumManyXI() {
let ptr = UnsafeMutablePointer<SinglePayloadEnumManyXI>.allocate(capacity: 1)
do {
let x = SinglePayloadEnumManyXI.nonEmpty(Builtin.zeroInitializer(), SimpleClass(x: 23))
testInit(ptr, to: x)
}
do {
let y = SinglePayloadEnumManyXI.nonEmpty(Builtin.zeroInitializer(), SimpleClass(x: 28))
// CHECK: Before deinit
print("Before deinit")
// CHECK-NEXT: SimpleClass deinitialized!
testAssign(ptr, from: y)
}
// CHECK-NEXT: Before deinit
print("Before deinit")
// CHECK-NEXT: SimpleClass deinitialized!
testDestroy(ptr)
ptr.deallocate()
}
testSinglePayloadEnumManyXI()
func testSinglePayloadEnumManyXIEmpty() {
let ptr = UnsafeMutablePointer<SinglePayloadEnumManyXI>.allocate(capacity: 1)
do {
let x = SinglePayloadEnumManyXI.empty0
testInit(ptr, to: x)
}
do {
let y = SinglePayloadEnumManyXI.empty1
// CHECK: Before deinit
print("Before deinit")
testAssign(ptr, from: y)
}
// CHECK-NEXT: Before deinit
print("Before deinit")
testDestroy(ptr)
ptr.deallocate()
}
testSinglePayloadEnumManyXIEmpty()
#if os(macOS)
func testObjc() {
let ptr = UnsafeMutablePointer<ObjcWrapper>.allocate(capacity: 1)