Skip to content

Commit 4f6ed73

Browse files
Merge pull request #72469 from nate-chandler/rdar125113258
[MoveOnlyAddressChecker] Fix enum representation.
2 parents e6a34e8 + a0a1ff2 commit 4f6ed73

File tree

8 files changed

+635
-361
lines changed

8 files changed

+635
-361
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 31 additions & 17 deletions
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 {
@@ -291,30 +306,27 @@ struct TypeTreeLeafTypeRange {
291306
/// The leaf type sub-range of the type tree of \p rootAddress, consisting of
292307
/// \p projectedAddress and all of \p projectedAddress's descendent fields in
293308
/// the type tree.
294-
///
295-
/// \returns None if we are unable to understand the path in between \p
296-
/// projectedAddress and \p rootAddress.
297-
static std::optional<TypeTreeLeafTypeRange> get(SILValue projectedValue,
298-
SILValue rootValue) {
309+
static void get(SILValue projectedValue, SILValue rootValue,
310+
SmallVectorImpl<TypeTreeLeafTypeRange> &ranges) {
299311
auto startEltOffset = SubElementOffset::compute(projectedValue, rootValue);
300312
if (!startEltOffset)
301-
return std::nullopt;
302-
return {{*startEltOffset,
303-
*startEltOffset + TypeSubElementCount(projectedValue)}};
313+
return;
314+
ranges.push_back({*startEltOffset,
315+
*startEltOffset + TypeSubElementCount(projectedValue)});
304316
}
305317

306318
/// Which bits of \p rootValue are involved in \p op.
307319
///
308320
/// This is a subset of (usually equal to) the bits of op->getType() in \p
309321
/// rootValue.
310-
static std::optional<TypeTreeLeafTypeRange> get(Operand *op,
311-
SILValue rootValue);
322+
static void get(Operand *op, SILValue rootValue,
323+
SmallVectorImpl<TypeTreeLeafTypeRange> &ranges);
312324

313325
static void constructProjectionsForNeededElements(
314-
SILValue rootValue, SILInstruction *insertPt,
326+
SILValue rootValue, SILInstruction *insertPt, DominanceInfo *domTree,
315327
SmallBitVector &neededElements,
316-
SmallVectorImpl<std::pair<SILValue, TypeTreeLeafTypeRange>>
317-
&resultingProjections);
328+
SmallVectorImpl<std::tuple<SILValue, TypeTreeLeafTypeRange,
329+
NeedsDestroy_t>> &resultingProjections);
318330

319331
static void visitContiguousRanges(
320332
SmallBitVector const &bits,
@@ -389,7 +401,9 @@ struct TypeTreeLeafTypeRange {
389401
/// common with filterBitVector.
390402
void constructFilteredProjections(
391403
SILValue value, SILInstruction *insertPt, SmallBitVector &filterBitVector,
392-
llvm::function_ref<bool(SILValue, TypeTreeLeafTypeRange)> callback);
404+
DominanceInfo *domTree,
405+
llvm::function_ref<bool(SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t)>
406+
callback);
393407

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

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 117 additions & 58 deletions
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);
@@ -473,33 +509,53 @@ void TypeTreeLeafTypeRange::constructFilteredProjections(
473509
llvm_unreachable("Not understand subtype");
474510
}
475511

476-
std::optional<TypeTreeLeafTypeRange>
477-
TypeTreeLeafTypeRange::get(Operand *op, SILValue rootValue) {
512+
void TypeTreeLeafTypeRange::get(
513+
Operand *op, SILValue rootValue,
514+
SmallVectorImpl<TypeTreeLeafTypeRange> &ranges) {
478515
auto projectedValue = op->get();
479516
auto startEltOffset = SubElementOffset::compute(projectedValue, rootValue);
480517
if (!startEltOffset)
481-
return std::nullopt;
518+
return;
482519

483520
// A drop_deinit only consumes the deinit bit of its operand.
484521
if (isa<DropDeinitInst>(op->getUser())) {
485522
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
486-
return {{upperBound - 1, upperBound}};
523+
ranges.push_back({upperBound - 1, upperBound});
524+
return;
487525
}
488526

489527
// An `inject_enum_addr` only initializes the enum tag.
490528
if (auto inject = dyn_cast<InjectEnumAddrInst>(op->getUser())) {
491529
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
492-
unsigned payloadUpperBound = 0;
493-
if (inject->getElement()->hasAssociatedValues()) {
494-
auto payloadTy = projectedValue->getType().getEnumElementType(
495-
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+
}
496535

497-
payloadUpperBound =
498-
*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;
499553
}
500554
// TODO: account for deinit component if enum has deinit.
501555
assert(!projectedValue->getType().isValueTypeWithDeinit());
502-
return {{payloadUpperBound, upperBound}};
556+
ranges.push_back(
557+
{*startEltOffset + numAtoms, *startEltOffset + numAtoms + 1});
558+
return;
503559
}
504560

505561
// Uses that borrow a value do not involve the deinit bit.
@@ -512,23 +568,23 @@ TypeTreeLeafTypeRange::get(Operand *op, SILValue rootValue) {
512568
deinitBitOffset = 1;
513569
}
514570

515-
return {{*startEltOffset, *startEltOffset +
516-
TypeSubElementCount(projectedValue) -
517-
deinitBitOffset}};
571+
ranges.push_back({*startEltOffset, *startEltOffset +
572+
TypeSubElementCount(projectedValue) -
573+
deinitBitOffset});
518574
}
519575

520576
void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
521-
SILValue rootValue, SILInstruction *insertPt,
577+
SILValue rootValue, SILInstruction *insertPt, DominanceInfo *domTree,
522578
SmallBitVector &neededElements,
523-
SmallVectorImpl<std::pair<SILValue, TypeTreeLeafTypeRange>>
579+
SmallVectorImpl<std::tuple<SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t>>
524580
&resultingProjections) {
525581
TypeTreeLeafTypeRange rootRange(rootValue);
526582
(void)rootRange;
527583
assert(rootRange.size() == neededElements.size());
528584

529-
StackList<std::pair<SILValue, TypeTreeLeafTypeRange>> worklist(
530-
insertPt->getFunction());
531-
worklist.push_back({rootValue, rootRange});
585+
StackList<std::tuple<SILValue, TypeTreeLeafTypeRange, NeedsDestroy_t>>
586+
worklist(insertPt->getFunction());
587+
worklist.push_back({rootValue, rootRange, NeedsDestroy});
532588

533589
// Temporary vector we use for our computation.
534590
SmallBitVector tmp(neededElements.size());
@@ -540,8 +596,10 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
540596

541597
while (!worklist.empty()) {
542598
auto pair = worklist.pop_back_val();
543-
auto value = pair.first;
544-
auto range = pair.second;
599+
SILValue value;
600+
TypeTreeLeafTypeRange range;
601+
NeedsDestroy_t needsDestroy;
602+
std::tie(value, range, needsDestroy) = pair;
545603

546604
tmp.reset();
547605
tmp.set(range.startEltOffset, range.endEltOffset);
@@ -558,17 +616,18 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements(
558616
// everything set in the range. In that case, we just add this range to the
559617
// result and continue.
560618
if (allInRange(tmp, range)) {
561-
resultingProjections.emplace_back(value, range);
619+
resultingProjections.emplace_back(value, range, needsDestroy);
562620
continue;
563621
}
564622

565623
// Otherwise, we have a partial range. We need to split our range and then
566624
// recursively process those ranges looking for subranges that have
567625
// completely set bits.
568626
range.constructFilteredProjections(
569-
value, insertPt, neededElements,
570-
[&](SILValue subType, TypeTreeLeafTypeRange range) -> bool {
571-
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});
572631
return true;
573632
});
574633
}

0 commit comments

Comments
 (0)