Skip to content

Commit 622f7a2

Browse files
authored
Merge pull request #67022 from drexin/wip-sp-gen-destr-inject-tag
Support destructiveInjectEnumTag in generic single payload enums with layout strings
2 parents e404c99 + 8ccaad1 commit 622f7a2

File tree

6 files changed

+168
-16
lines changed

6 files changed

+168
-16
lines changed

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,7 +2333,7 @@ FUNCTION(SingletonEnumGetEnumTag,
23332333
C_CC, AlwaysAvailable,
23342334
RETURNS(Int32Ty),
23352335
ARGS(Int8PtrTy, TypeMetadataPtrTy),
2336-
ATTRS(NoUnwind),
2336+
ATTRS(NoUnwind, WillReturn),
23372337
EFFECT(NoEffect))
23382338

23392339
// unsigned swift_enumSimple_getEnumTag(swift::OpaqueValue *address,
@@ -2343,7 +2343,7 @@ FUNCTION(EnumSimpleGetEnumTag,
23432343
C_CC, AlwaysAvailable,
23442344
RETURNS(Int32Ty),
23452345
ARGS(Int8PtrTy, TypeMetadataPtrTy),
2346-
ATTRS(NoUnwind),
2346+
ATTRS(NoUnwind, WillReturn),
23472347
EFFECT(NoEffect))
23482348

23492349
// unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address,
@@ -2353,7 +2353,7 @@ FUNCTION(EnumFnGetEnumTag,
23532353
C_CC, AlwaysAvailable,
23542354
RETURNS(Int32Ty),
23552355
ARGS(Int8PtrTy, TypeMetadataPtrTy),
2356-
ATTRS(NoUnwind),
2356+
ATTRS(NoUnwind, WillReturn),
23572357
EFFECT(NoEffect))
23582358

23592359
// unsigned swift_multiPayloadEnumGeneric_getEnumTag(opaque* address,
@@ -2363,7 +2363,7 @@ FUNCTION(MultiPayloadEnumGenericGetEnumTag,
23632363
C_CC, AlwaysAvailable,
23642364
RETURNS(Int32Ty),
23652365
ARGS(Int8PtrTy, TypeMetadataPtrTy),
2366-
ATTRS(NoUnwind),
2366+
ATTRS(NoUnwind, WillReturn),
23672367
EFFECT(NoEffect))
23682368

23692369
// unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
@@ -2373,7 +2373,18 @@ FUNCTION(SinglePayloadEnumGenericGetEnumTag,
23732373
C_CC, AlwaysAvailable,
23742374
RETURNS(Int32Ty),
23752375
ARGS(Int8PtrTy, TypeMetadataPtrTy),
2376-
ATTRS(NoUnwind),
2376+
ATTRS(NoUnwind, WillReturn),
2377+
EFFECT(NoEffect))
2378+
2379+
// void swift_singlePayloadEnumGeneric_destructiveInjectEnumTag(swift::OpaqueValue *address,
2380+
// unsigned tag,
2381+
// const Metadata *metadata)
2382+
FUNCTION(SinglePayloadEnumGenericDestructiveInjectEnumTag,
2383+
swift_singlePayloadEnumGeneric_destructiveInjectEnumTag,
2384+
C_CC, AlwaysAvailable,
2385+
RETURNS(VoidTy),
2386+
ARGS(Int8PtrTy, Int32Ty, TypeMetadataPtrTy),
2387+
ATTRS(NoUnwind, WillReturn),
23772388
EFFECT(NoEffect))
23782389

23792390
// void swift_generic_instantiateLayoutString(const uint8_t* layoutStr,

lib/IRGen/GenValueWitness.cpp

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,38 @@ static llvm::Constant *getEnumTagFunction(IRGenModule &IGM,
931931
}
932932
}
933933

934+
static llvm::Constant *
935+
getDestructiveInjectEnumTagFunction(IRGenModule &IGM,
936+
const EnumTypeLayoutEntry *typeLayoutEntry,
937+
GenericSignature genericSig) {
938+
if ((!typeLayoutEntry->layoutString(IGM, genericSig) &&
939+
!isRuntimeInstatiatedLayoutString(IGM, typeLayoutEntry)) ||
940+
typeLayoutEntry->isSingleton()) {
941+
return nullptr;
942+
} else if (!typeLayoutEntry->isFixedSize(IGM)) {
943+
if (typeLayoutEntry->isMultiPayloadEnum()) {
944+
return nullptr;
945+
} else {
946+
return IGM.getSinglePayloadEnumGenericDestructiveInjectEnumTagFn();
947+
}
948+
} else if (typeLayoutEntry->isMultiPayloadEnum()) {
949+
return nullptr;
950+
} else {
951+
auto &payloadTI = **(typeLayoutEntry->cases[0]->getFixedTypeInfo());
952+
auto mask = payloadTI.getFixedExtraInhabitantMask(IGM);
953+
auto tzCount = mask.countTrailingZeros();
954+
auto shiftedMask = mask.lshr(tzCount);
955+
auto toCount = shiftedMask.countTrailingOnes();
956+
if (payloadTI.mayHaveExtraInhabitants(IGM) &&
957+
(mask.countPopulation() > 64 || toCount != mask.countPopulation() ||
958+
(tzCount % toCount != 0))) {
959+
return nullptr;
960+
} else {
961+
return nullptr;
962+
}
963+
}
964+
}
965+
934966
static bool
935967
valueWitnessRequiresCopyability(ValueWitness index) {
936968
switch (index) {
@@ -1189,8 +1221,31 @@ addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B, ValueWitness index,
11891221
}
11901222
goto standard;
11911223
}
1224+
case ValueWitness::DestructiveInjectEnumTag: {
1225+
assert(concreteType.getEnumOrBoundGenericEnum());
1226+
if (IGM.Context.LangOpts.hasFeature(Feature::LayoutStringValueWitnesses) &&
1227+
IGM.getOptions().EnableLayoutStringValueWitnesses) {
1228+
auto ty = boundGenericCharacteristics
1229+
? boundGenericCharacteristics->concreteType
1230+
: concreteType;
1231+
auto &typeInfo = boundGenericCharacteristics
1232+
? *boundGenericCharacteristics->TI
1233+
: concreteTI;
1234+
if (auto *typeLayoutEntry = typeInfo.buildTypeLayoutEntry(
1235+
IGM, ty, /*useStructLayouts*/ true)) {
1236+
if (auto *enumLayoutEntry = typeLayoutEntry->getAsEnum()) {
1237+
auto genericSig = concreteType.getNominalOrBoundGenericNominal()
1238+
->getGenericSignature();
1239+
if (auto *fn = getDestructiveInjectEnumTagFunction(
1240+
IGM, enumLayoutEntry, genericSig)) {
1241+
return addFunction(fn);
1242+
}
1243+
}
1244+
}
1245+
}
1246+
goto standard;
1247+
}
11921248
case ValueWitness::DestructiveProjectEnumData:
1193-
case ValueWitness::DestructiveInjectEnumTag:
11941249
assert(concreteType.getEnumOrBoundGenericEnum());
11951250
goto standard;
11961251
}

stdlib/public/runtime/BytecodeLayouts.cpp

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -751,29 +751,29 @@ static inline T handleSinglePayloadEnumGenericTag(
751751
LayoutStringReader &reader, uint8_t *addr,
752752
std::function<std::optional<T>(const Metadata *, size_t, uint8_t)>
753753
extraTagBytesHandler,
754-
std::function<T(const Metadata *, unsigned, unsigned)> xiHandler) {
754+
std::function<T(const Metadata *, unsigned, unsigned, size_t, uint8_t)>
755+
xiHandler) {
755756
auto tagBytesAndOffset = reader.readBytes<uint64_t>();
756757
auto extraTagBytesPattern = (uint8_t)(tagBytesAndOffset >> 62);
757758
auto xiTagBytesOffset =
758759
tagBytesAndOffset & std::numeric_limits<uint32_t>::max();
759-
const Metadata *xiType = nullptr;
760+
auto numExtraTagBytes = 1 << (extraTagBytesPattern - 1);
761+
auto payloadSize = reader.readBytes<size_t>();
762+
auto xiType = reader.readBytes<const Metadata *>();
760763

761764
if (extraTagBytesPattern) {
762-
auto numExtraTagBytes = 1 << (extraTagBytesPattern - 1);
763-
auto payloadSize = reader.readBytes<size_t>();
764-
xiType = reader.readBytes<const Metadata *>();
765765
if (auto result =
766766
extraTagBytesHandler(xiType, payloadSize, numExtraTagBytes)) {
767767
return *result;
768768
}
769769
} else {
770770
reader.skip(sizeof(size_t));
771-
xiType = reader.readBytes<const Metadata *>();
772771
}
773772

774773
auto numEmptyCases = reader.readBytes<unsigned>();
775774

776-
return xiHandler(xiType, xiTagBytesOffset, numEmptyCases);
775+
return xiHandler(xiType, xiTagBytesOffset, numEmptyCases, payloadSize,
776+
numExtraTagBytes);
777777
}
778778

779779
extern "C" unsigned
@@ -803,7 +803,8 @@ swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
803803
};
804804

805805
auto xihandler = [addr](const Metadata *xiType, unsigned xiTagBytesOffset,
806-
unsigned numEmptyCases) -> unsigned {
806+
unsigned numEmptyCases, size_t payloadSize,
807+
uint8_t numExtraTagBytes) -> unsigned {
807808
if (xiType) {
808809
return xiType->vw_getEnumTagSinglePayload(
809810
(const OpaqueValue *)(addr + xiTagBytesOffset), numEmptyCases);
@@ -816,6 +817,64 @@ swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
816817
reader, addr, extraTagBytesHandler, xihandler);
817818
}
818819

820+
extern "C" void swift_singlePayloadEnumGeneric_destructiveInjectEnumTag(
821+
swift::OpaqueValue *address, unsigned tag, const Metadata *metadata) {
822+
auto addr = reinterpret_cast<uint8_t *>(address);
823+
LayoutStringReader reader{metadata->getLayoutString(),
824+
layoutStringHeaderSize + sizeof(uint64_t)};
825+
826+
auto extraTagBytesHandler =
827+
[=](const Metadata *xiType, size_t payloadSize,
828+
uint8_t numExtraTagBytes) -> std::optional<bool> {
829+
unsigned payloadNumExtraInhabitants =
830+
xiType ? xiType->vw_getNumExtraInhabitants() : 0;
831+
if (tag <= payloadNumExtraInhabitants) {
832+
return std::nullopt;
833+
}
834+
835+
unsigned noPayloadIndex = tag - 1;
836+
unsigned caseIndex = noPayloadIndex - payloadNumExtraInhabitants;
837+
unsigned payloadIndex, extraTagIndex;
838+
if (payloadSize >= 4) {
839+
extraTagIndex = 1;
840+
payloadIndex = caseIndex;
841+
} else {
842+
unsigned payloadBits = payloadSize * 8U;
843+
extraTagIndex = 1U + (caseIndex >> payloadBits);
844+
payloadIndex = caseIndex & ((1U << payloadBits) - 1U);
845+
}
846+
847+
// Store into the value.
848+
if (payloadSize)
849+
storeEnumElement(addr, payloadIndex, payloadSize);
850+
if (numExtraTagBytes)
851+
storeEnumElement(addr + payloadSize, extraTagIndex, numExtraTagBytes);
852+
853+
return true;
854+
};
855+
856+
auto xihandler = [=](const Metadata *xiType, unsigned xiTagBytesOffset,
857+
unsigned numEmptyCases, size_t payloadSize,
858+
uint8_t numExtraTagBytes) -> bool {
859+
unsigned payloadNumExtraInhabitants =
860+
xiType ? xiType->vw_getNumExtraInhabitants() : 0;
861+
if (tag <= payloadNumExtraInhabitants) {
862+
if (numExtraTagBytes != 0)
863+
storeEnumElement(addr + payloadSize, 0, numExtraTagBytes);
864+
865+
if (tag == 0)
866+
return true;
867+
868+
xiType->vw_storeEnumTagSinglePayload(
869+
(swift::OpaqueValue *)(addr + xiTagBytesOffset), tag, numEmptyCases);
870+
}
871+
return true;
872+
};
873+
874+
handleSinglePayloadEnumGenericTag<bool>(reader, addr, extraTagBytesHandler,
875+
xihandler);
876+
}
877+
819878
extern "C" swift::OpaqueValue *
820879
swift_generic_initializeBufferWithCopyOfBuffer(swift::ValueBuffer *dest,
821880
swift::ValueBuffer *src,

stdlib/public/runtime/BytecodeLayouts.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ SWIFT_RUNTIME_EXPORT
133133
unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
134134
const Metadata *metadata);
135135
SWIFT_RUNTIME_EXPORT
136+
void swift_singlePayloadEnumGeneric_destructiveInjectEnumTag(
137+
swift::OpaqueValue *address, unsigned tag, const Metadata *metadata);
138+
SWIFT_RUNTIME_EXPORT
136139
void swift_generic_instantiateLayoutString(const uint8_t *layoutStr,
137140
Metadata *type);
138141

test/Interpreter/Inputs/layout_string_witnesses_types_resilient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public struct GenericResilient<C, T> {
2222
public enum ResilientSinglePayloadEnumGeneric<T> {
2323
case empty0
2424
case empty1
25-
case nonEmpty0(T)
25+
case nonEmpty(T)
2626
}
2727

2828
public enum ResilientMultiPayloadEnumGeneric<T> {

test/Interpreter/layout_string_witnesses_dynamic.swift

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ testResilientMultiPayloadEnumTag()
569569

570570
func testResilientSinglePayloadEnumGenericTag() {
571571
let x = switch getResilientSinglePayloadEnumGenericEmpty0(AnyObject.self) {
572-
case .nonEmpty0: 0
572+
case .nonEmpty: 0
573573
case .empty0: 1
574574
case .empty1: 2
575575
}
@@ -594,6 +594,30 @@ func testResilientMultiPayloadEnumGenericTag() {
594594

595595
testResilientMultiPayloadEnumGenericTag()
596596

597+
@inline(never)
598+
func matchResilientSinglePayloadEnumGenericTag(_ x: ResilientSinglePayloadEnumGeneric<AnyObject>) -> Int {
599+
return switch x {
600+
case .nonEmpty: 0
601+
case .empty0: 1
602+
case .empty1: 2
603+
}
604+
}
605+
606+
func testResilientSinglePayloadEnumGenericInjectTag() {
607+
let x = ResilientSinglePayloadEnumGeneric<AnyObject>.nonEmpty(SimpleClass(x: 23))
608+
let y = ResilientSinglePayloadEnumGeneric<AnyObject>.empty0
609+
let z = ResilientSinglePayloadEnumGeneric<AnyObject>.empty1
610+
611+
// CHECK: Enum case: 0
612+
print("Enum case: \(matchResilientSinglePayloadEnumGenericTag(x))")
613+
// CHECK: Enum case: 1
614+
print("Enum case: \(matchResilientSinglePayloadEnumGenericTag(y))")
615+
// CHECK: Enum case: 2
616+
print("Enum case: \(matchResilientSinglePayloadEnumGenericTag(z))")
617+
}
618+
619+
testResilientSinglePayloadEnumGenericInjectTag()
620+
597621
#if os(macOS)
598622

599623
import Foundation

0 commit comments

Comments
 (0)