Skip to content

Commit a0a1ff2

Browse files
committed
[MoveOnlyAddressChecker] Fix enum repr.
Change FieldSensitive's enum representation to allow distinguishing among the elements with associated value. Consider `unchecked_take_enum_data_addr` to consume all other fields than that taken. rdar://125113258
1 parent 53bffd2 commit a0a1ff2

File tree

6 files changed

+232
-80
lines changed

6 files changed

+232
-80
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

+24-7
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,19 @@ struct SubElementOffset {
228228
computeForValue(SILValue projectionFromRoot, SILValue rootValue);
229229
};
230230

231-
/// Given a type T, this is the number of leaf field types in T's type tree. A
232-
/// leaf field type is a descendent field of T that does not have any
233-
/// descendent's itself.
231+
/// Counts the leaf fields aggregated together into a particular type.
232+
///
233+
/// Defined in such a way as to enable walking up the tree of aggregations
234+
/// node-by-node, visiting each type along the way.
235+
///
236+
/// The definition is given recursively as follows:
237+
/// a an atom => count(a) := 1
238+
/// t a tuple => count(t) := sum(t.elements, { elt in count(type(elt)) })
239+
/// s a struct => count(s) := sum(s.fields, { f in count(type(f)) })
240+
/// + s.hasDeinit
241+
/// e an enum => count(e) := sum(e.elements, { elt in count(type(elt)) })
242+
/// + e.hasDeinit
243+
/// + 1 // discriminator
234244
struct TypeSubElementCount {
235245
unsigned number;
236246

@@ -267,6 +277,11 @@ struct TypeSubElementCount {
267277

268278
class FieldSensitivePrunedLiveness;
269279

280+
enum NeedsDestroy_t {
281+
DoesNotNeedDestroy = false,
282+
NeedsDestroy = true,
283+
};
284+
270285
/// A span of leaf elements in the sub-element break down of the linearization
271286
/// of the type tree of a type T.
272287
struct TypeTreeLeafTypeRange {
@@ -308,10 +323,10 @@ struct TypeTreeLeafTypeRange {
308323
SmallVectorImpl<TypeTreeLeafTypeRange> &ranges);
309324

310325
static void constructProjectionsForNeededElements(
311-
SILValue rootValue, SILInstruction *insertPt,
326+
SILValue rootValue, SILInstruction *insertPt, DominanceInfo *domTree,
312327
SmallBitVector &neededElements,
313-
SmallVectorImpl<std::pair<SILValue, TypeTreeLeafTypeRange>>
314-
&resultingProjections);
328+
SmallVectorImpl<std::tuple<SILValue, TypeTreeLeafTypeRange,
329+
NeedsDestroy_t>> &resultingProjections);
315330

316331
static void visitContiguousRanges(
317332
SmallBitVector const &bits,
@@ -386,7 +401,9 @@ struct TypeTreeLeafTypeRange {
386401
/// common with filterBitVector.
387402
void constructFilteredProjections(
388403
SILValue value, SILInstruction *insertPt, SmallBitVector &filterBitVector,
389-
llvm::function_ref<bool(SILValue, TypeTreeLeafTypeRange)> callback);
404+
DominanceInfo *domTree,
405+
llvm::function_ref<bool(SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t)>
406+
callback);
390407

391408
void print(llvm::raw_ostream &os) const {
392409
os << "TypeTreeLeafTypeRange: (start: " << startEltOffset

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

+107-51
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,20 @@ TypeSubElementCount::TypeSubElementCount(SILType type, SILModule &mod,
8383
return;
8484
}
8585

86-
// If we have an enum, we add one for tracking if the base enum is set and use
87-
// the remaining bits for the max sized payload. This ensures that if we have
88-
// a smaller sized payload, we still get all of the bits set, allowing for a
89-
// homogeneous representation.
9086
if (auto *enumDecl = type.getEnumOrBoundGenericEnum()) {
9187
unsigned numElements = 0;
9288
for (auto *eltDecl : enumDecl->getAllElements()) {
9389
if (!eltDecl->hasAssociatedValues())
9490
continue;
9591
auto elt = type.getEnumElementType(eltDecl, mod, context);
96-
numElements = std::max(numElements,
97-
unsigned(TypeSubElementCount(elt, mod, context)));
92+
numElements += unsigned(TypeSubElementCount(elt, mod, context));
9893
}
9994
number = numElements + 1;
95+
if (type.isValueTypeWithDeinit()) {
96+
// 'self' has its own liveness represented as an additional field at the
97+
// end of the structure.
98+
++number;
99+
}
100100
return;
101101
}
102102

@@ -190,19 +190,20 @@ SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
190190
continue;
191191
}
192192

193-
// In the case of enums, we note that our representation is:
194-
//
195-
// ---------|Enum| ---
196-
// / \
197-
// / \
198-
// v v
199-
// |Bits for Max Sized Payload| |Discrim Bit|
200-
//
201-
// So our payload is always going to start at the current field number since
202-
// we are the left most child of our parent enum. So we just need to look
203-
// through to our parent enum.
204193
if (auto *enumData = dyn_cast<UncheckedTakeEnumDataAddrInst>(
205194
projectionDerivedFromRoot)) {
195+
auto ty = enumData->getOperand()->getType();
196+
auto *enumDecl = enumData->getEnumDecl();
197+
for (auto *element : enumDecl->getAllElements()) {
198+
if (!element->hasAssociatedValues())
199+
continue;
200+
if (element == enumData->getElement())
201+
break;
202+
auto context = TypeExpansionContext(*rootAddress->getFunction());
203+
auto elementTy = ty.getEnumElementType(element, mod, context);
204+
finalSubElementOffset +=
205+
unsigned(TypeSubElementCount(elementTy, mod, context));
206+
}
206207
projectionDerivedFromRoot = enumData->getOperand();
207208
continue;
208209
}
@@ -376,7 +377,9 @@ SubElementOffset::computeForValue(SILValue projectionDerivedFromRoot,
376377

377378
void TypeTreeLeafTypeRange::constructFilteredProjections(
378379
SILValue value, SILInstruction *insertPt, SmallBitVector &filterBitVector,
379-
llvm::function_ref<bool(SILValue, TypeTreeLeafTypeRange)> callback) {
380+
DominanceInfo *domTree,
381+
llvm::function_ref<bool(SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t)>
382+
callback) {
380383
auto *fn = insertPt->getFunction();
381384
SILType type = value->getType();
382385

@@ -408,7 +411,7 @@ void TypeTreeLeafTypeRange::constructFilteredProjections(
408411

409412
auto newValue =
410413
builder.createStructElementAddr(insertPt->getLoc(), value, varDecl);
411-
callback(newValue, TypeTreeLeafTypeRange(start, next));
414+
callback(newValue, TypeTreeLeafTypeRange(start, next), NeedsDestroy);
412415
start = next;
413416
}
414417
if (type.isValueTypeWithDeinit()) {
@@ -419,30 +422,63 @@ void TypeTreeLeafTypeRange::constructFilteredProjections(
419422
return;
420423
}
421424

422-
// We only allow for enums that can be completely destroyed. If there is code
423-
// where an enum should be partially destroyed, we need to treat the
424-
// unchecked_take_enum_data_addr as a separate value whose liveness we are
425-
// tracking.
426425
if (auto *enumDecl = type.getEnumOrBoundGenericEnum()) {
426+
struct ElementRecord {
427+
EnumElementDecl *element;
428+
unsigned start;
429+
unsigned next;
430+
};
431+
SmallVector<ElementRecord, 2> projectedElements;
427432
unsigned start = startEltOffset;
428-
429-
unsigned maxSubEltCount = 0;
430433
for (auto *eltDecl : enumDecl->getAllElements()) {
431434
if (!eltDecl->hasAssociatedValues())
432435
continue;
436+
433437
auto nextType = type.getEnumElementType(eltDecl, fn);
434-
maxSubEltCount =
435-
std::max(maxSubEltCount, unsigned(TypeSubElementCount(nextType, fn)));
438+
unsigned next = start + TypeSubElementCount(nextType, fn);
439+
if (noneSet(filterBitVector, start, next)) {
440+
start = next;
441+
continue;
442+
}
443+
444+
projectedElements.push_back({eltDecl, start, next});
445+
start = next;
436446
}
437447

438-
// Add a bit for the case bit.
439-
unsigned next = maxSubEltCount + 1;
448+
// Add a bit for the discriminator.
449+
unsigned next = start + 1;
450+
451+
if (!allSet(filterBitVector, start, next)) {
452+
for (auto record : projectedElements) {
453+
// Find a preexisting unchecked_take_enum_data_addr that dominates
454+
// insertPt.
455+
bool foundProjection = false;
456+
for (auto *user : value->getUsers()) {
457+
auto *utedai = dyn_cast<UncheckedTakeEnumDataAddrInst>(user);
458+
if (!utedai) {
459+
continue;
460+
}
461+
if (utedai->getElement() == record.element) {
462+
continue;
463+
}
464+
if (!domTree->dominates(utedai, insertPt)) {
465+
continue;
466+
}
440467

441-
// Make sure we are all set.
442-
assert(allSet(filterBitVector, start, next));
468+
callback(utedai, TypeTreeLeafTypeRange(record.start, record.next),
469+
DoesNotNeedDestroy);
470+
foundProjection = true;
471+
}
472+
assert(foundProjection ||
473+
llvm::count_if(enumDecl->getAllElements(), [](auto *elt) {
474+
return elt->hasAssociatedValues();
475+
}) == 1);
476+
}
477+
return;
478+
}
443479

444480
// Then just pass back our enum base value as the pointer.
445-
callback(value, TypeTreeLeafTypeRange(start, next));
481+
callback(value, TypeTreeLeafTypeRange(start, next), NeedsDestroy);
446482

447483
// Then set start to next and assert we covered the entire end elt offset.
448484
start = next;
@@ -463,7 +499,7 @@ void TypeTreeLeafTypeRange::constructFilteredProjections(
463499

464500
auto newValue =
465501
builder.createTupleElementAddr(insertPt->getLoc(), value, index);
466-
callback(newValue, TypeTreeLeafTypeRange(start, next));
502+
callback(newValue, TypeTreeLeafTypeRange(start, next), NeedsDestroy);
467503
start = next;
468504
}
469505
assert(start == endEltOffset);
@@ -491,17 +527,34 @@ void TypeTreeLeafTypeRange::get(
491527
// An `inject_enum_addr` only initializes the enum tag.
492528
if (auto inject = dyn_cast<InjectEnumAddrInst>(op->getUser())) {
493529
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
494-
unsigned payloadUpperBound = 0;
495-
if (inject->getElement()->hasAssociatedValues()) {
496-
auto payloadTy = projectedValue->getType().getEnumElementType(
497-
inject->getElement(), op->getFunction());
530+
// TODO: account for deinit component if enum has deinit.
531+
assert(!projectedValue->getType().isValueTypeWithDeinit());
532+
ranges.push_back({upperBound - 1, upperBound});
533+
return;
534+
}
498535

499-
payloadUpperBound =
500-
*startEltOffset + TypeSubElementCount(payloadTy, op->getFunction());
536+
if (auto *utedai = dyn_cast<UncheckedTakeEnumDataAddrInst>(op->getUser())) {
537+
auto *selected = utedai->getElement();
538+
auto *enumDecl = utedai->getEnumDecl();
539+
unsigned numAtoms = 0;
540+
for (auto *element : enumDecl->getAllElements()) {
541+
if (!element->hasAssociatedValues()) {
542+
continue;
543+
}
544+
auto elementTy = projectedValue->getType().getEnumElementType(
545+
element, op->getFunction());
546+
auto elementAtoms =
547+
unsigned(TypeSubElementCount(elementTy, op->getFunction()));
548+
if (element != selected) {
549+
ranges.push_back({*startEltOffset + numAtoms,
550+
*startEltOffset + numAtoms + elementAtoms});
551+
}
552+
numAtoms += elementAtoms;
501553
}
502554
// TODO: account for deinit component if enum has deinit.
503555
assert(!projectedValue->getType().isValueTypeWithDeinit());
504-
ranges.push_back({payloadUpperBound, upperBound});
556+
ranges.push_back(
557+
{*startEltOffset + numAtoms, *startEltOffset + numAtoms + 1});
505558
return;
506559
}
507560

@@ -521,17 +574,17 @@ void TypeTreeLeafTypeRange::get(
521574
}
522575

523576
void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
524-
SILValue rootValue, SILInstruction *insertPt,
577+
SILValue rootValue, SILInstruction *insertPt, DominanceInfo *domTree,
525578
SmallBitVector &neededElements,
526-
SmallVectorImpl<std::pair<SILValue, TypeTreeLeafTypeRange>>
579+
SmallVectorImpl<std::tuple<SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t>>
527580
&resultingProjections) {
528581
TypeTreeLeafTypeRange rootRange(rootValue);
529582
(void)rootRange;
530583
assert(rootRange.size() == neededElements.size());
531584

532-
StackList<std::pair<SILValue, TypeTreeLeafTypeRange>> worklist(
533-
insertPt->getFunction());
534-
worklist.push_back({rootValue, rootRange});
585+
StackList<std::tuple<SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t>>
586+
worklist(insertPt->getFunction());
587+
worklist.push_back({rootValue, rootRange, NeedsDestroy});
535588

536589
// Temporary vector we use for our computation.
537590
SmallBitVector tmp(neededElements.size());
@@ -543,8 +596,10 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
543596

544597
while (!worklist.empty()) {
545598
auto pair = worklist.pop_back_val();
546-
auto value = pair.first;
547-
auto range = pair.second;
599+
SILValue value;
600+
TypeTreeLeafTypeRange range;
601+
NeedsDestroy_t needsDestroy;
602+
std::tie(value, range, needsDestroy) = pair;
548603

549604
tmp.reset();
550605
tmp.set(range.startEltOffset, range.endEltOffset);
@@ -561,17 +616,18 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
561616
// everything set in the range. In that case, we just add this range to the
562617
// result and continue.
563618
if (allInRange(tmp, range)) {
564-
resultingProjections.emplace_back(value, range);
619+
resultingProjections.emplace_back(value, range, needsDestroy);
565620
continue;
566621
}
567622

568623
// Otherwise, we have a partial range. We need to split our range and then
569624
// recursively process those ranges looking for subranges that have
570625
// completely set bits.
571626
range.constructFilteredProjections(
572-
value, insertPt, neededElements,
573-
[&](SILValue subType, TypeTreeLeafTypeRange range) -> bool {
574-
worklist.push_back({subType, range});
627+
value, insertPt, neededElements, domTree,
628+
[&](SILValue subType, TypeTreeLeafTypeRange range,
629+
NeedsDestroy_t needsDestroy) -> bool {
630+
worklist.push_back({subType, range, needsDestroy});
575631
return true;
576632
});
577633
}

0 commit comments

Comments
 (0)