Skip to content

[Runtime+IRGen] Add layout string support for generic singleton enums #66647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/Runtime/Enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ void swift_initEnumMetadataSingleCase(EnumMetadata *enumType,
EnumLayoutFlags flags,
const TypeLayout *payload);

SWIFT_RUNTIME_EXPORT
void swift_initEnumMetadataSingleCaseWithLayoutString(
EnumMetadata *self, EnumLayoutFlags layoutFlags,
const Metadata *payloadType);

/// Initialize the type metadata for a single-payload enum type.
///
/// \param enumType - pointer to the instantiated but uninitialized metadata
Expand Down
11 changes: 11 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,17 @@ FUNCTION(InitEnumMetadataSingleCase,
ATTRS(NoUnwind, WillReturn),
EFFECT(MetaData))

// void swift_initEnumMetadataSingleCaseWithLayoutString(Metadata *enumType,
// EnumLayoutFlags flags,
// Metadata *payload);
FUNCTION(InitEnumMetadataSingleCaseWithLayoutString,
swift_initEnumMetadataSingleCaseWithLayoutString,
C_CC, AlwaysAvailable,
RETURNS(VoidTy),
ARGS(TypeMetadataPtrTy, SizeTy, TypeMetadataPtrTy),
ATTRS(NoUnwind, WillReturn),
EFFECT(MetaData))

// void swift_initEnumMetadataSinglePayload(Metadata *enumType,
// EnumLayoutFlags flags,
// TypeLayout *payload,
Expand Down
75 changes: 48 additions & 27 deletions lib/IRGen/GenEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,13 +663,42 @@ namespace {
metadata);
}

void initializeMetadataWithLayoutString(IRGenFunction &IGF,
llvm::Value *metadata,
bool isVWTMutable,
SILType T,
MetadataDependencyCollector *collector) const override {
// Not yet supported on this type, so forward to regular method
initializeMetadata(IGF, metadata, isVWTMutable, T, collector);
void initializeMetadataWithLayoutString(
IRGenFunction &IGF, llvm::Value *metadata, bool isVWTMutable, SILType T,
MetadataDependencyCollector *collector) const override {
if (TIK >= Fixed)
return;

assert(ElementsWithPayload.size() == 1 &&
"empty singleton enum should not be dynamic!");

auto payloadTy =
T.getEnumElementType(ElementsWithPayload[0].decl, IGM.getSILModule(),
IGM.getMaximalTypeExpansionContext());

auto request = DynamicMetadataRequest::getNonBlocking(
MetadataState::LayoutComplete, collector);
auto payloadMetadata =
IGF.emitTypeMetadataRefForLayout(payloadTy, request);

auto flags = emitEnumLayoutFlags(IGF.IGM, isVWTMutable);
IGF.Builder.CreateCall(
IGF.IGM
.getInitEnumMetadataSingleCaseWithLayoutStringFunctionPointer(),
{metadata, flags, payloadMetadata});

// Pre swift-5.1 runtimes were missing the initialization of the
// the extraInhabitantCount field. Do it here instead.
auto payloadLayout = emitTypeLayoutRef(IGF, payloadTy, collector);
auto payloadRef = IGF.Builder.CreateBitOrPointerCast(
payloadLayout, IGF.IGM.TypeLayoutTy->getPointerTo());
auto payloadExtraInhabitantCount =
IGF.Builder.CreateLoad(IGF.Builder.CreateStructGEP(
Address(payloadRef, IGF.IGM.TypeLayoutTy, Alignment(1)), 3,
Size(IGF.IGM.DataLayout.getTypeAllocSize(IGF.IGM.SizeTy) * 2 +
IGF.IGM.DataLayout.getTypeAllocSize(IGF.IGM.Int32Ty))));
emitStoreOfExtraInhabitantCount(IGF, payloadExtraInhabitantCount,
metadata);
}

bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
Expand Down Expand Up @@ -973,11 +1002,9 @@ namespace {
// witness table initialization.
}

void initializeMetadataWithLayoutString(IRGenFunction &IGF,
llvm::Value *metadata,
bool isVWTMutable,
SILType T,
MetadataDependencyCollector *collector) const override {
void initializeMetadataWithLayoutString(
IRGenFunction &IGF, llvm::Value *metadata, bool isVWTMutable, SILType T,
MetadataDependencyCollector *collector) const override {
// No-payload enums are always fixed-size so never need dynamic value
// witness table initialization.
}
Expand Down Expand Up @@ -3207,11 +3234,9 @@ namespace {
{metadata, flags, payloadLayout, emptyCasesVal});
}

void initializeMetadataWithLayoutString(IRGenFunction &IGF,
llvm::Value *metadata,
bool isVWTMutable,
SILType T,
MetadataDependencyCollector *collector) const override {
void initializeMetadataWithLayoutString(
IRGenFunction &IGF, llvm::Value *metadata, bool isVWTMutable, SILType T,
MetadataDependencyCollector *collector) const override {
// Not yet supported on this type, so forward to regular method
initializeMetadata(IGF, metadata, isVWTMutable, T, collector);
}
Expand Down Expand Up @@ -5335,11 +5360,9 @@ namespace {
{metadata, flags, numPayloadsVal, payloadLayoutArray});
}

void initializeMetadataWithLayoutString(IRGenFunction &IGF,
llvm::Value *metadata,
bool isVWTMutable,
SILType T,
MetadataDependencyCollector *collector) const override {
void initializeMetadataWithLayoutString(
IRGenFunction &IGF, llvm::Value *metadata, bool isVWTMutable, SILType T,
MetadataDependencyCollector *collector) const override {
// Fixed-size enums don't need dynamic metadata initialization.
if (TIK >= Fixed) return;

Expand Down Expand Up @@ -6035,11 +6058,9 @@ namespace {
llvm_unreachable("resilient enums cannot be defined");
}

void initializeMetadataWithLayoutString(IRGenFunction &IGF,
llvm::Value *metadata,
bool isVWTMutable,
SILType T,
MetadataDependencyCollector *collector) const override {
void initializeMetadataWithLayoutString(
IRGenFunction &IGF, llvm::Value *metadata, bool isVWTMutable, SILType T,
MetadataDependencyCollector *collector) const override {
llvm_unreachable("resilient enums cannot be defined");
}

Expand Down
11 changes: 7 additions & 4 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5737,14 +5737,17 @@ namespace {
}

auto &strategy = getEnumImplStrategy(IGM, getLoweredType());
bool isSupportedCase = strategy.getElementsWithPayload().size() > 1 ||
(strategy.getElementsWithPayload().size() == 1 &&
strategy.getElementsWithNoPayload().empty());

return !!getLayoutString() ||
(IGM.Context.LangOpts.hasFeature(
Feature::LayoutStringValueWitnessesInstantiation) &&
Feature::LayoutStringValueWitnessesInstantiation) &&
IGM.getOptions().EnableLayoutStringValueWitnessesInstantiation &&
(HasDependentVWT || HasDependentMetadata) &&
!isa<FixedTypeInfo>(IGM.getTypeInfo(getLoweredType())) &&
strategy.getElementsWithPayload().size() > 1);
(HasDependentVWT || HasDependentMetadata) &&
!isa<FixedTypeInfo>(IGM.getTypeInfo(getLoweredType())) &&
isSupportedCase);
}

llvm::Constant *emitNominalTypeDescriptor() {
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenValueWitness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM,
Feature::LayoutStringValueWitnessesInstantiation) &&
IGM.getOptions().EnableLayoutStringValueWitnessesInstantiation) {
if (auto *enumEntry = typeLayoutEntry->getAsEnum()) {
return enumEntry->isMultiPayloadEnum();
return enumEntry->isMultiPayloadEnum() || enumEntry->isSingleton();
}
return (typeLayoutEntry->isAlignedGroup() &&
!typeLayoutEntry->isFixedSize(IGM));
Expand Down
57 changes: 57 additions & 0 deletions stdlib/public/runtime/Enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,61 @@ swift::swift_initEnumMetadataSingleCase(EnumMetadata *self,
vwtable->publishLayout(layout);
}

void swift::swift_initEnumMetadataSingleCaseWithLayoutString(
EnumMetadata *self, EnumLayoutFlags layoutFlags,
const Metadata *payloadType) {
assert(self->hasLayoutString());

auto payloadLayout = payloadType->getTypeLayout();
auto vwtable = getMutableVWTableForInit(self, layoutFlags);

TypeLayout layout;
layout.size = payloadLayout->size;
layout.stride = payloadLayout->stride;
layout.flags = payloadLayout->flags.withEnumWitnesses(true);
layout.extraInhabitantCount = payloadLayout->getNumExtraInhabitants();

auto refCountBytes = _swift_refCountBytesForMetatype(payloadType);
const size_t fixedLayoutStringSize =
layoutStringHeaderSize + sizeof(uint64_t) * 2;

uint8_t *layoutStr =
(uint8_t *)MetadataAllocator(LayoutStringTag)
.Allocate(fixedLayoutStringSize + refCountBytes, alignof(uint8_t));

size_t layoutStrOffset = sizeof(uint64_t);
writeBytes(layoutStr, layoutStrOffset, refCountBytes);
size_t fullOffset = 0;
size_t previousFieldOffset = 0;
LayoutStringFlags flags = LayoutStringFlags::Empty;

_swift_addRefCountStringForMetatype(layoutStr, layoutStrOffset, flags,
payloadType, fullOffset,
previousFieldOffset);

writeBytes(layoutStr, layoutStrOffset, (uint64_t)previousFieldOffset);
writeBytes(layoutStr, layoutStrOffset, (uint64_t)0);

// we mask out HasRelativePointers, because at this point they have all been
// resolved to metadata pointers
layoutStrOffset = 0;
writeBytes(layoutStr, layoutStrOffset,
((uint64_t)flags) &
~((uint64_t)LayoutStringFlags::HasRelativePointers));

vwtable->destroy = swift_generic_destroy;
vwtable->initializeWithCopy = swift_generic_initWithCopy;
vwtable->initializeWithTake = swift_generic_initWithTake;
vwtable->assignWithCopy = swift_generic_assignWithCopy;
vwtable->assignWithTake = swift_generic_assignWithTake;

installCommonValueWitnesses(layout, vwtable);

self->setLayoutString(layoutStr);

vwtable->publishLayout(layout);
}

void
swift::swift_initEnumMetadataSinglePayload(EnumMetadata *self,
EnumLayoutFlags layoutFlags,
Expand Down Expand Up @@ -221,6 +276,8 @@ void swift::swift_initEnumMetadataMultiPayloadWithLayoutString(
EnumLayoutFlags layoutFlags,
unsigned numPayloads,
const Metadata * const *payloadLayouts) {
assert(enumType->hasLayoutString());

// Accumulate the layout requirements of the payloads.
size_t payloadSize = 0, alignMask = 0;
bool isPOD = true, isBT = true;
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2739,8 +2739,8 @@ void swift::swift_initStructMetadataWithLayoutString(
previousFieldOffset);
}

writeBytes(layoutStr, layoutStrOffset, previousFieldOffset);
writeBytes(layoutStr, layoutStrOffset, 0);
writeBytes(layoutStr, layoutStrOffset, (uint64_t)previousFieldOffset);
writeBytes(layoutStr, layoutStrOffset, (uint64_t)0);

// we mask out HasRelativePointers, because at this point they have all been
// resolved to metadata pointers
Expand Down
4 changes: 4 additions & 0 deletions test/Interpreter/Inputs/layout_string_witnesses_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@ public struct InternalEnumWrapper {
}
}

public enum SingletonEnum<T> {
case only(T, Int)
}

public enum SinglePayloadEnumManyXI {
case empty0
case empty1
Expand Down
28 changes: 28 additions & 0 deletions test/Interpreter/layout_string_witnesses_dynamic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,34 @@ func testGenericEnum() {

testGenericEnum()

func testGenericEnumSingleton() {
let ptr = allocateInternalGenericPtr(of: SingletonEnum<TestClass>.self)

do {
let x = TestClass()
testGenericInit(ptr, to: SingletonEnum<TestClass>.only(x, 23))
}

do {
let y = TestClass()
// CHECK: Before deinit
print("Before deinit")

// CHECK-NEXT: TestClass deinitialized!
testGenericAssign(ptr, from: SingletonEnum<TestClass>.only(y, 32))
}

// CHECK-NEXT: Before deinit
print("Before deinit")

// CHECK-NEXT: TestClass deinitialized!
testGenericDestroy(ptr, of: SingletonEnum<TestClass>.self)

ptr.deallocate()
}

testGenericEnumSingleton()

func testRecursive() {
let ptr = allocateInternalGenericPtr(of: Recursive<TestClass>.self)

Expand Down