Skip to content

Commit 5cbf9f9

Browse files
authored
[IRGen] Handle complex single payload enum cases (swiftlang#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
1 parent a5f0dbc commit 5cbf9f9

File tree

8 files changed

+299
-18
lines changed

8 files changed

+299
-18
lines changed

lib/IRGen/IRGenMangler.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,16 @@ std::string IRGenMangler::mangleSymbolNameForMangledConformanceAccessorString(
400400
return finalize();
401401
}
402402

403+
std::string IRGenMangler::mangleSymbolNameForMangledGetEnumTagForLayoutString(
404+
CanType type) {
405+
beginManglingWithoutPrefix();
406+
Buffer << "get_enum_tag_for_layout_string"
407+
<< " ";
408+
409+
appendType(type, nullptr);
410+
return finalize();
411+
}
412+
403413
std::string IRGenMangler::mangleSymbolNameForUnderlyingTypeAccessorString(
404414
OpaqueTypeDecl *opaque, unsigned index) {
405415
beginManglingWithoutPrefix();

lib/IRGen/IRGenMangler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ class IRGenMangler : public Mangle::ASTMangler {
633633
CanType type,
634634
ProtocolConformanceRef conformance);
635635

636+
std::string mangleSymbolNameForMangledGetEnumTagForLayoutString(CanType type);
637+
636638
std::string
637639
mangleSymbolNameForUnderlyingTypeAccessorString(OpaqueTypeDecl *opaque,
638640
unsigned index);

lib/IRGen/TypeLayout.cpp

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class LayoutStringBuilder {
7676
Existential = 0x0e,
7777
Resilient = 0x0f,
7878
SinglePayloadEnumSimple = 0x10,
79+
SinglePayloadEnumFN = 0x11,
80+
SinglePayloadEnumFNResolved = 0x12,
7981

8082
Skip = 0x80,
8183
// We may use the MSB as flag that a count follows,
@@ -93,12 +95,19 @@ class LayoutStringBuilder {
9395
const TypeLayoutEntry *payload;
9496
};
9597

98+
struct SinglePayloadEnumFN {
99+
llvm::Function *tagFn;
100+
unsigned extraTagByteCount;
101+
const TypeLayoutEntry *payload;
102+
};
103+
96104
struct RefCounting {
97105
RefCountingKind kind;
98106
union {
99107
size_t size;
100108
llvm::Function* metaTypeRef;
101109
SinglePayloadEnumSimple singlePayloadEnumSimple;
110+
SinglePayloadEnumFN singlePayloadEnumFN;
102111
};
103112

104113
RefCounting() = default;
@@ -132,6 +141,16 @@ class LayoutStringBuilder {
132141
refCountings.push_back(op);
133142
}
134143

144+
void addSinglePayloadEnumFN(llvm::Function *tagFn, unsigned extraTagByteCount,
145+
const TypeLayoutEntry *payload) {
146+
RefCounting op;
147+
op.kind = RefCountingKind::SinglePayloadEnumFN;
148+
op.singlePayloadEnumFN.tagFn = tagFn;
149+
op.singlePayloadEnumSimple.extraTagByteCount = extraTagByteCount;
150+
op.singlePayloadEnumFN.payload = payload;
151+
refCountings.push_back(op);
152+
}
153+
135154
void addSkip(size_t size) {
136155
if (refCountings.empty() ||
137156
refCountings.back().kind != RefCountingKind::Skip) {
@@ -227,6 +246,40 @@ class LayoutStringBuilder {
227246
break;
228247
}
229248

249+
case RefCountingKind::SinglePayloadEnumFN: {
250+
uint64_t op = (static_cast<uint64_t>(refCounting.kind) << 56) | skip;
251+
B.addInt64(op);
252+
253+
skip = 0;
254+
size_t nestedRefCountBytes = 0;
255+
256+
auto enumData = refCounting.singlePayloadEnumFN;
257+
258+
B.addRelativeOffset(IGM.IntPtrTy, enumData.tagFn);
259+
260+
auto nestedRefCountBytesPlaceholder =
261+
B.addPlaceholderWithSize(IGM.SizeTy);
262+
auto skipBytesPlaceholder = B.addPlaceholderWithSize(IGM.SizeTy);
263+
264+
LayoutStringBuilder nestedBuilder{};
265+
enumData.payload->refCountString(IGM, nestedBuilder, genericSig);
266+
addRefCountings(IGM, B, genericSig, nestedBuilder.refCountings, skip,
267+
nestedRefCountBytes, flags);
268+
269+
auto nestedSkip = enumData.payload->fixedSize(IGM)->getValue() - skip;
270+
B.fillPlaceholderWithInt(nestedRefCountBytesPlaceholder, IGM.SizeTy,
271+
nestedRefCountBytes);
272+
B.fillPlaceholderWithInt(skipBytesPlaceholder, IGM.SizeTy, nestedSkip);
273+
274+
refCountBytes += (sizeof(uint64_t)) +
275+
(3 * IGM.getPointerSize().getValue()) +
276+
nestedRefCountBytes;
277+
skip += enumData.extraTagByteCount;
278+
279+
flags |= LayoutStringFlags::HasRelativePointers;
280+
break;
281+
}
282+
230283
default: {
231284
uint64_t op = (static_cast<uint64_t>(refCounting.kind) << 56) | skip;
232285
B.addInt64(op);
@@ -362,6 +415,30 @@ llvm::Function *createMetatypeAccessorFunction(IRGenModule &IGM, SILType ty,
362415
return static_cast<llvm::Function *>(helperFn);
363416
}
364417

418+
llvm::Function *createFixedEnumLoadTag(IRGenModule &IGM,
419+
const EnumTypeLayoutEntry &entry) {
420+
assert(entry.isFixedSize(IGM));
421+
422+
IRGenMangler mangler;
423+
auto symbol = mangler.mangleSymbolNameForMangledGetEnumTagForLayoutString(
424+
entry.ty.getASTType());
425+
426+
auto helperFn = IGM.getOrCreateHelperFunction(
427+
symbol, IGM.Int32Ty /*retTy*/, IGM.Int8PtrTy /*argTys*/,
428+
[&](IRGenFunction &IGF) {
429+
auto enumPtr = IGF.collectParameters().claimNext();
430+
auto *typeInfo = *entry.fixedTypeInfo;
431+
auto enumType = typeInfo->getStorageType()->getPointerTo();
432+
auto castEnumPtr = IGF.Builder.CreateBitCast(enumPtr, enumType);
433+
auto enumAddr = typeInfo->getAddressForPointer(castEnumPtr);
434+
435+
auto tag = entry.getEnumTag(IGF, enumAddr);
436+
IGF.Builder.CreateRet(tag);
437+
});
438+
439+
return static_cast<llvm::Function *>(helperFn);
440+
}
441+
365442
TypeLayoutEntry::~TypeLayoutEntry() {}
366443

367444
void TypeLayoutEntry::computeProperties() {
@@ -2069,6 +2146,7 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString(
20692146
uint64_t zeroTagValue = 0;
20702147
unsigned xiBitCount = 0;
20712148
unsigned xiOffset = 0;
2149+
bool isSimple = true;
20722150

20732151
auto &payloadTI = **cases[0]->getFixedTypeInfo();
20742152

@@ -2088,12 +2166,12 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString(
20882166
(tzCount % toCount != 0)) {
20892167
// We currently don't handle cases with non-contiguous or > 64 bits of
20902168
// extra inhabitants
2091-
return false;
2169+
isSimple = false;
2170+
} else {
2171+
xiBitCount = std::min(64u, mask.countPopulation());
2172+
xiOffset = mask.countTrailingZeros();
2173+
zeroTagValue = lowValue.extractBitsAsZExtValue(xiBitCount, xiOffset);
20922174
}
2093-
2094-
xiBitCount = std::min(64u, mask.countPopulation());
2095-
xiOffset = mask.countTrailingZeros();
2096-
zeroTagValue = lowValue.extractBitsAsZExtValue(xiBitCount, xiOffset);
20972175
}
20982176
}
20992177

@@ -2119,8 +2197,13 @@ bool EnumTypeLayoutEntry::buildSinglePayloadRefCountString(
21192197
return false;
21202198
}
21212199

2122-
B.addSinglePayloadEnumSimple(zeroTagValue, xiTagValues, extraTagByteCount,
2123-
xiBitCount / 8, xiOffset, cases[0]);
2200+
if (isSimple) {
2201+
B.addSinglePayloadEnumSimple(zeroTagValue, xiTagValues, extraTagByteCount,
2202+
xiBitCount / 8, xiOffset, cases[0]);
2203+
} else {
2204+
auto tagFn = createFixedEnumLoadTag(IGM, *this);
2205+
B.addSinglePayloadEnumFN(tagFn, extraTagByteCount, cases[0]);
2206+
}
21242207

21252208
return true;
21262209
}

stdlib/public/runtime/BytecodeLayouts.cpp

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/Support/SwapByteOrder.h"
2727
#include <cstdint>
2828
#include <limits>
29+
#include <type_traits>
2930
#if SWIFT_OBJC_INTEROP
3031
#include "swift/Runtime/ObjCBridge.h"
3132
#include <Block.h>
@@ -60,24 +61,33 @@ static Metadata *getExistentialTypeMetadata(OpaqueValue *object) {
6061
return reinterpret_cast<Metadata**>(object)[NumWords_ValueBuffer];
6162
}
6263

63-
typedef Metadata* (*MetadataAccessor)(const Metadata* const *);
64+
template <typename FnTy>
65+
static const FnTy readRelativeFunctionPointer(const uint8_t *layoutStr,
66+
size_t &offset) {
67+
static_assert(std::is_pointer<FnTy>::value);
6468

65-
static const Metadata *getResilientTypeMetadata(const Metadata* metadata,
66-
const uint8_t *layoutStr,
67-
size_t &offset) {
6869
auto absolute = layoutStr + offset;
6970
auto relativeOffset =
7071
(uintptr_t)(intptr_t)(int32_t)readBytes<intptr_t>(layoutStr, offset);
71-
MetadataAccessor fn;
72+
FnTy fn;
7273

7374
#if SWIFT_PTRAUTH
74-
fn = (MetadataAccessor)ptrauth_sign_unauthenticated(
75+
fn = (FnTy)ptrauth_sign_unauthenticated(
7576
(void *)((uintptr_t)absolute + relativeOffset),
7677
ptrauth_key_function_pointer, 0);
7778
#else
78-
fn = (MetadataAccessor)((uintptr_t)absolute + relativeOffset);
79+
fn = (FnTy)((uintptr_t)absolute + relativeOffset);
7980
#endif
8081

82+
return fn;
83+
}
84+
85+
typedef Metadata *(*MetadataAccessor)(const Metadata *const *);
86+
87+
static const Metadata *getResilientTypeMetadata(const Metadata *metadata,
88+
const uint8_t *layoutStr,
89+
size_t &offset) {
90+
auto fn = readRelativeFunctionPointer<MetadataAccessor>(layoutStr, offset);
8191
return fn(metadata->getGenericArgs());
8292
}
8393

@@ -117,6 +127,13 @@ inline static bool handleNextRefCount(const Metadata *metadata, const uint8_t *t
117127
} else if (SWIFT_UNLIKELY(tag ==
118128
RefCountingKind::SinglePayloadEnumSimple)) {
119129
Handler::handleSinglePayloadEnumSimple(typeLayout, offset, addrOffset, std::forward<Params>(params)...);
130+
} else if (SWIFT_UNLIKELY(tag == RefCountingKind::SinglePayloadEnumFN)) {
131+
Handler::handleSinglePayloadEnumFN(typeLayout, offset, false, addrOffset,
132+
std::forward<Params>(params)...);
133+
} else if (SWIFT_UNLIKELY(tag ==
134+
RefCountingKind::SinglePayloadEnumFNResolved)) {
135+
Handler::handleSinglePayloadEnumFN(typeLayout, offset, true, addrOffset,
136+
std::forward<Params>(params)...);
120137
} else {
121138
Handler::handleReference(tag, addrOffset, std::forward<Params>(params)...);
122139
}
@@ -154,8 +171,9 @@ static uint64_t readTagBytes(uint8_t *addr, uint8_t byteCount) {
154171
}
155172
}
156173

157-
static void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, size_t &offset,
158-
uint8_t *addr, size_t &addrOffset) {
174+
static void handleSinglePayloadEnumSimple(const uint8_t *typeLayout,
175+
size_t &offset, uint8_t *addr,
176+
size_t &addrOffset) {
159177
auto byteCountsAndOffset = readBytes<uint64_t>(typeLayout, offset);
160178
auto extraTagBytesPattern = (uint8_t)(byteCountsAndOffset >> 62);
161179
auto xiTagBytesPattern = ((uint8_t)(byteCountsAndOffset >> 59)) & 0x7;
@@ -198,6 +216,30 @@ static void handleSinglePayloadEnumSimple(const uint8_t *typeLayout, size_t &off
198216
addrOffset += skip;
199217
}
200218

219+
typedef unsigned (*GetEnumTagFn)(const uint8_t *);
220+
221+
static void handleSinglePayloadEnumFN(const uint8_t *typeLayout, size_t &offset,
222+
bool resolved, uint8_t *addr,
223+
size_t &addrOffset) {
224+
GetEnumTagFn getEnumTag;
225+
if (resolved) {
226+
getEnumTag = readBytes<GetEnumTagFn>(typeLayout, offset);
227+
} else {
228+
getEnumTag = readRelativeFunctionPointer<GetEnumTagFn>(typeLayout, offset);
229+
}
230+
231+
unsigned enumTag = getEnumTag(addr + addrOffset);
232+
233+
if (enumTag == 0) {
234+
offset += sizeof(size_t) * 2;
235+
} else {
236+
auto refCountBytes = readBytes<size_t>(typeLayout, offset);
237+
auto skip = readBytes<size_t>(typeLayout, offset);
238+
offset += refCountBytes;
239+
addrOffset += skip;
240+
}
241+
}
242+
201243
const DestroyFuncAndMask destroyTable[] = {
202244
{(DestrFn)&skipDestroy, false},
203245
{(DestrFn)&swift_errorRelease, true},
@@ -232,6 +274,13 @@ struct DestroyHandler {
232274
::handleSinglePayloadEnumSimple(typeLayout, offset, addr, addrOffset);
233275
}
234276

277+
static inline void handleSinglePayloadEnumFN(const uint8_t *typeLayout,
278+
size_t &offset, bool resolved,
279+
size_t &addrOffset,
280+
uint8_t *addr) {
281+
::handleSinglePayloadEnumFN(typeLayout, offset, resolved, addr, addrOffset);
282+
}
283+
235284
static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *addr) {
236285
const auto &destroyFunc = destroyTable[static_cast<uint8_t>(tag)];
237286
if (SWIFT_LIKELY(destroyFunc.isIndirect)) {
@@ -305,6 +354,14 @@ struct CopyHandler {
305354
::handleSinglePayloadEnumSimple(typeLayout, offset, (uint8_t *)src, addrOffset);
306355
}
307356

357+
static inline void handleSinglePayloadEnumFN(const uint8_t *typeLayout,
358+
size_t &offset, bool resolved,
359+
size_t &addrOffset,
360+
uint8_t *dest, uint8_t *src) {
361+
::handleSinglePayloadEnumFN(typeLayout, offset, resolved, (uint8_t *)src,
362+
addrOffset);
363+
}
364+
308365
static inline void handleReference(RefCountingKind tag, uintptr_t addrOffset, uint8_t *dest, uint8_t *src) {
309366
const auto &retainFunc = retainTable[static_cast<uint8_t>(tag)];
310367
if (SWIFT_LIKELY(retainFunc.isSingle)) {
@@ -388,6 +445,18 @@ swift_generic_initWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src,
388445
break;
389446
}
390447

448+
case RefCountingKind::SinglePayloadEnumFN: {
449+
handleSinglePayloadEnumFN(typeLayout, offset, false, (uint8_t *)src,
450+
addrOffset);
451+
break;
452+
}
453+
454+
case RefCountingKind::SinglePayloadEnumFNResolved: {
455+
handleSinglePayloadEnumFN(typeLayout, offset, true, (uint8_t *)src,
456+
addrOffset);
457+
break;
458+
}
459+
391460
case RefCountingKind::End:
392461
return dest;
393462
default:
@@ -439,6 +508,25 @@ void swift::swift_resolve_resilientAccessors(
439508
case RefCountingKind::SinglePayloadEnumSimple:
440509
i += (3 * sizeof(uint64_t)) + (4 * sizeof(size_t));
441510
break;
511+
512+
case RefCountingKind::SinglePayloadEnumFN: {
513+
auto getEnumTag =
514+
readRelativeFunctionPointer<GetEnumTagFn>(fieldLayoutStr, i);
515+
size_t writeOffset =
516+
layoutStrOffset + currentOffset - layoutStringHeaderSize;
517+
uint64_t tagAndOffset =
518+
(((uint64_t)RefCountingKind::SinglePayloadEnumFNResolved) << 56) |
519+
size;
520+
writeBytes(layoutStr, writeOffset, tagAndOffset);
521+
writeBytes(layoutStr, writeOffset, getEnumTag);
522+
i += 2 * sizeof(size_t);
523+
break;
524+
}
525+
526+
case RefCountingKind::SinglePayloadEnumFNResolved:
527+
i += 3 * sizeof(size_t);
528+
break;
529+
442530
default:
443531
break;
444532
}

stdlib/public/runtime/BytecodeLayouts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ enum class RefCountingKind : uint8_t {
4444
Resilient = 0x0f,
4545

4646
SinglePayloadEnumSimple = 0x10,
47+
SinglePayloadEnumFN = 0x11,
48+
SinglePayloadEnumFNResolved = 0x12,
4749

4850
Skip = 0x80,
4951
// We may use the MSB as flag that a count follows,

test/Interpreter/Inputs/layout_string_witnesses_types.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,17 @@ public struct InternalEnumWrapper {
442442
}
443443
}
444444

445+
public enum SinglePayloadEnumManyXI {
446+
case empty0
447+
case empty1
448+
case empty2
449+
case empty3
450+
case empty4
451+
case empty5
452+
case empty6
453+
case nonEmpty(Builtin.Int127, SimpleClass)
454+
}
455+
445456
public struct PrespecializedStruct<T> {
446457
let y: Int = 0
447458
let x: T

0 commit comments

Comments
 (0)