Skip to content

Commit 270089f

Browse files
authored
[IRGen+Runtime] Layout string getEnumTag for fixed size enums subset (#66899)
* [IRGen+Runtime] Layout string getEnumTag for fixed size enums subset getEnumTag impl for layout strings of fixed sized enums that use a function to fetch the enum tag * Fix potential UB in IRGen
1 parent a44c77d commit 270089f

File tree

6 files changed

+111
-9
lines changed

6 files changed

+111
-9
lines changed

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2315,6 +2315,16 @@ FUNCTION(GenericInitWithTake,
23152315
ATTRS(NoUnwind),
23162316
EFFECT(Refcounting))
23172317

2318+
// unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address,
2319+
// const Metadata *metadata);
2320+
FUNCTION(EnumFnGetEnumTag,
2321+
swift_enumFn_getEnumTag,
2322+
C_CC, AlwaysAvailable,
2323+
RETURNS(Int32Ty),
2324+
ARGS(Int8PtrTy, TypeMetadataPtrTy),
2325+
ATTRS(NoUnwind),
2326+
EFFECT(NoEffect))
2327+
23182328
// unsigned swift_multiPayloadEnumGeneric_getEnumTag(opaque* address,
23192329
// const Metadata *type);
23202330
FUNCTION(MultiPayloadEnumGenericGetEnumTag,
@@ -2325,7 +2335,8 @@ FUNCTION(MultiPayloadEnumGenericGetEnumTag,
23252335
ATTRS(NoUnwind),
23262336
EFFECT(NoEffect))
23272337

2328-
// void swift_generic_instantiateLayoutString(const uint8_t* layoutStr, Metadata* type);
2338+
// void swift_generic_instantiateLayoutString(const uint8_t* layoutStr,
2339+
// Metadata* type);
23292340
FUNCTION(GenericInstantiateLayoutString,
23302341
swift_generic_instantiateLayoutString,
23312342
C_CC, AlwaysAvailable,

lib/IRGen/GenValueWitness.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -885,8 +885,8 @@ void addStride(ConstantStructBuilder &B, const TypeInfo *TI, IRGenModule &IGM) {
885885
}
886886
} // end anonymous namespace
887887

888-
bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM,
889-
const TypeLayoutEntry *typeLayoutEntry) {
888+
static bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM,
889+
const TypeLayoutEntry *typeLayoutEntry) {
890890
if (IGM.Context.LangOpts.hasFeature(Feature::LayoutStringValueWitnesses) &&
891891
IGM.Context.LangOpts.hasFeature(
892892
Feature::LayoutStringValueWitnessesInstantiation) &&
@@ -899,6 +899,36 @@ bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM,
899899
return false;
900900
}
901901

902+
static llvm::Constant *getEnumTagFunction(IRGenModule &IGM,
903+
const EnumTypeLayoutEntry *typeLayoutEntry) {
904+
if (typeLayoutEntry->isSingleton()) {
905+
return nullptr;
906+
} else if (!typeLayoutEntry->isFixedSize(IGM)) {
907+
if (typeLayoutEntry->isMultiPayloadEnum()) {
908+
return IGM.getMultiPayloadEnumGenericGetEnumTagFn();
909+
} else {
910+
// TODO: implement support for single payload generic enums
911+
return nullptr;
912+
}
913+
} else if (typeLayoutEntry->isMultiPayloadEnum()) {
914+
return IGM.getEnumFnGetEnumTagFn();
915+
} else {
916+
auto &payloadTI = **(typeLayoutEntry->cases[0]->getFixedTypeInfo());
917+
auto mask = payloadTI.getFixedExtraInhabitantMask(IGM);
918+
auto tzCount = mask.countTrailingZeros();
919+
auto shiftedMask = mask.lshr(tzCount);
920+
auto toCount = shiftedMask.countTrailingOnes();
921+
if (payloadTI.mayHaveExtraInhabitants(IGM) &&
922+
(mask.countPopulation() > 64 ||
923+
toCount != mask.countPopulation() ||
924+
(tzCount % toCount != 0))) {
925+
return IGM.getEnumFnGetEnumTagFn();
926+
} else {
927+
return nullptr;
928+
}
929+
}
930+
}
931+
902932
static bool
903933
valueWitnessRequiresCopyability(ValueWitness index) {
904934
switch (index) {
@@ -1127,9 +1157,8 @@ static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B,
11271157
if (auto *typeLayoutEntry = typeInfo.buildTypeLayoutEntry(
11281158
IGM, ty, /*useStructLayouts*/ true)) {
11291159
if (auto *enumLayoutEntry = typeLayoutEntry->getAsEnum()) {
1130-
if (enumLayoutEntry->isMultiPayloadEnum() &&
1131-
!typeLayoutEntry->isFixedSize(IGM)) {
1132-
return addFunction(IGM.getMultiPayloadEnumGenericGetEnumTagFn());
1160+
if (auto *fn = getEnumTagFunction(IGM, enumLayoutEntry)) {
1161+
return addFunction(fn);
11331162
}
11341163
}
11351164
}

stdlib/public/runtime/BytecodeLayouts.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,17 @@ swift_generic_assignWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src,
652652
return swift_generic_initWithTake(dest, src, metadata);
653653
}
654654

655+
extern "C"
656+
unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address,
657+
const Metadata *metadata) {
658+
auto addr = reinterpret_cast<const uint8_t *>(address);
659+
LayoutStringReader reader{metadata->getLayoutString(),
660+
layoutStringHeaderSize + sizeof(uint64_t)};
661+
auto getEnumTag = readRelativeFunctionPointer<GetEnumTagFn>(reader);
662+
663+
return getEnumTag(addr);
664+
}
665+
655666
extern "C" unsigned
656667
swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
657668
const Metadata *metadata) {

stdlib/public/runtime/BytecodeLayouts.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ swift::OpaqueValue *swift_generic_initWithTake(swift::OpaqueValue *dest,
113113
swift::OpaqueValue *src,
114114
const Metadata *metadata);
115115
SWIFT_RUNTIME_EXPORT
116+
unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address,
117+
const Metadata *metadata);
118+
SWIFT_RUNTIME_EXPORT
116119
unsigned swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address,
117120
const Metadata *metadata);
118121
SWIFT_RUNTIME_EXPORT

test/Interpreter/Inputs/layout_string_witnesses_types_resilient.swift

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,34 @@ public struct GenericResilient<C, T> {
1919
}
2020
}
2121

22-
public enum ResilientMultiPayloadEnum<T> {
22+
public enum ResilientMultiPayloadEnumGeneric<T> {
2323
case empty0
2424
case empty1
2525
case nonEmpty0(AnyObject)
2626
case nonEmpty1(T)
2727
}
2828

29-
public func getResilientMultiPayloadEnumEmpty0<T>(_ t: T.Type) -> ResilientMultiPayloadEnum<T> {
29+
public enum ResilientMultiPayloadEnum {
30+
case empty0
31+
case empty1
32+
case nonEmpty0(AnyObject, Bool)
33+
case nonEmpty1(AnyObject)
34+
}
35+
36+
public enum ResilientSinglePayloadEnumComplex {
37+
case empty0
38+
case empty1
39+
case nonEmpty(ResilientMultiPayloadEnum)
40+
}
41+
42+
public func getResilientMultiPayloadEnumGenericEmpty0<T>(_ t: T.Type) -> ResilientMultiPayloadEnumGeneric<T> {
43+
return .empty0
44+
}
45+
46+
public func getResilientMultiPayloadEnumEmpty0() -> ResilientMultiPayloadEnum {
47+
return .empty0
48+
}
49+
50+
public func getResilientSinglePayloadEnumComplexEmpty0() -> ResilientSinglePayloadEnumComplex {
3051
return .empty0
3152
}

test/Interpreter/layout_string_witnesses_dynamic.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,21 @@ func testGenericSinglePayloadEnumManyXI() {
516516

517517
testGenericSinglePayloadEnumManyXI()
518518

519+
func testResilientSinglePayloadEnumComplexTag() {
520+
let x = switch getResilientSinglePayloadEnumComplexEmpty0() {
521+
case .nonEmpty: 0
522+
case .empty0: 1
523+
case .empty1: 2
524+
}
525+
526+
// CHECK: Enum case: 1
527+
print("Enum case: \(x)")
528+
}
529+
530+
testResilientSinglePayloadEnumComplexTag()
531+
519532
func testResilientMultiPayloadEnumTag() {
520-
let x = switch getResilientMultiPayloadEnumEmpty0(AnyObject.self) {
533+
let x = switch getResilientMultiPayloadEnumEmpty0() {
521534
case .nonEmpty0: 0
522535
case .nonEmpty1: 1
523536
case .empty0: 2
@@ -530,6 +543,20 @@ func testResilientMultiPayloadEnumTag() {
530543

531544
testResilientMultiPayloadEnumTag()
532545

546+
func testResilientMultiPayloadEnumGenericTag() {
547+
let x = switch getResilientMultiPayloadEnumGenericEmpty0(AnyObject.self) {
548+
case .nonEmpty0: 0
549+
case .nonEmpty1: 1
550+
case .empty0: 2
551+
case .empty1: 3
552+
}
553+
554+
// CHECK: Enum case: 2
555+
print("Enum case: \(x)")
556+
}
557+
558+
testResilientMultiPayloadEnumGenericTag()
559+
533560
#if os(macOS)
534561

535562
import Foundation

0 commit comments

Comments
 (0)