Skip to content

Commit 4cb3f58

Browse files
committed
[move-only-addr] Separate handling of pure takes from copies we must turn into takes.
We technically don't need to do this, but I think that it makes the code easier to understand since one doesn't need to deal with the cognitive dissonance caused by dealing with copies from the take array.
1 parent 5fb001c commit 4cb3f58

File tree

1 file changed

+106
-42
lines changed

1 file changed

+106
-42
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 106 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ struct UseState {
292292
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> livenessUses;
293293
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> borrows;
294294
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> takeInsts;
295+
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> copyInsts;
295296
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> initInsts;
296297
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> reinitInsts;
297298

@@ -301,6 +302,7 @@ struct UseState {
301302
destroys.clear();
302303
livenessUses.clear();
303304
borrows.clear();
305+
copyInsts.clear();
304306
takeInsts.clear();
305307
initInsts.clear();
306308
reinitInsts.clear();
@@ -552,6 +554,9 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
552554
// At this point, we have handled all of the non-loadTakeOrCopy/consuming
553555
// uses.
554556
if (auto *copyAddr = dyn_cast<CopyAddrInst>(user)) {
557+
assert(op->getOperandNumber() == CopyAddrInst::Src &&
558+
"Should have dest above in memInstMust{Rei,I}nitialize");
559+
555560
if (markedValue->getCheckKind() == MarkMustCheckInst::CheckKind::NoCopy) {
556561
LLVM_DEBUG(llvm::dbgs()
557562
<< "Found mark must check [nocopy] error: " << *user);
@@ -564,8 +569,13 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
564569
if (!leafRange)
565570
return false;
566571

567-
LLVM_DEBUG(llvm::dbgs() << "Found take: " << *user);
568-
useState.takeInsts.insert({user, *leafRange});
572+
if (copyAddr->isTakeOfSrc()) {
573+
LLVM_DEBUG(llvm::dbgs() << "Found take: " << *user);
574+
useState.takeInsts.insert({user, *leafRange});
575+
} else {
576+
LLVM_DEBUG(llvm::dbgs() << "Found copy: " << *user);
577+
useState.copyInsts.insert({user, *leafRange});
578+
}
569579
return true;
570580
}
571581

@@ -637,7 +647,7 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
637647
}
638648

639649
// Then if we had any final consuming uses, mark that this liveness use is
640-
// a take and if not, mark this as a borrow.
650+
// a take/copy and if not, mark this as a borrow.
641651
auto leafRange = TypeTreeLeafTypeRange::get(op->get(), getRootAddress());
642652
if (!leafRange)
643653
return false;
@@ -652,14 +662,23 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
652662

653663
useState.borrows.insert({user, *leafRange});
654664
} else {
655-
LLVM_DEBUG(llvm::dbgs() << "Found take inst: " << *user);
656-
useState.takeInsts.insert({user, *leafRange});
665+
// If we had a load [copy], store this into the copy list. These are the
666+
// things that we must merge into destroy_addr or reinits after we are
667+
// done checking. The load [take] are already complete and good to go.
668+
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
669+
LLVM_DEBUG(llvm::dbgs() << "Found take inst: " << *user);
670+
useState.takeInsts.insert({user, *leafRange});
671+
} else {
672+
LLVM_DEBUG(llvm::dbgs() << "Found copy inst: " << *user);
673+
useState.copyInsts.insert({user, *leafRange});
674+
}
657675
}
658676
return true;
659677
}
660678
}
661679

662-
// Now that we have handled or loadTakeOrCopy, we need to now track our Takes.
680+
// Now that we have handled or loadTakeOrCopy, we need to now track our
681+
// additional pure takes.
663682
if (::memInstMustConsume(op)) {
664683
auto leafRange = TypeTreeLeafTypeRange::get(op->get(), getRootAddress());
665684
if (!leafRange)
@@ -956,6 +975,65 @@ void BlockSummaries::summarize(SILBasicBlock &block) {
956975
}
957976
}
958977

978+
{
979+
auto iter = addressUseState.copyInsts.find(&inst);
980+
if (iter != addressUseState.copyInsts.end()) {
981+
// If we are not yet tracking a "take up" or a "liveness up", then we
982+
// can update our state. In those other two cases we emit an error
983+
// diagnostic below.
984+
if (!state.isTrackingAnyState()) {
985+
LLVM_DEBUG(llvm::dbgs()
986+
<< " Tracking new take up: " << *iter->first);
987+
state.trackTake(iter->first, iter->second);
988+
continue;
989+
}
990+
991+
bool emittedSomeDiagnostic = false;
992+
if (auto *takeUp = state.getTakeUp()) {
993+
LLVM_DEBUG(llvm::dbgs() << " Found two takes, emitting error!\n");
994+
LLVM_DEBUG(llvm::dbgs() << " First take: " << *takeUp);
995+
LLVM_DEBUG(llvm::dbgs() << " Second take: " << inst);
996+
diagnosticEmitter.emitAddressDiagnostic(markedAddress, takeUp, &inst,
997+
true /*is consuming*/);
998+
emittedSomeDiagnostic = true;
999+
}
1000+
1001+
// If we found a liveness inst, we are going to emit an error since we
1002+
// have a use after free.
1003+
if (auto *livenessUpInst = state.getLivenessUp()) {
1004+
// If we are tracking state for an inout and our liveness inst is a
1005+
// function exiting instruction, we want to emit a special
1006+
// diagnostic error saying that the user has not reinitialized inout
1007+
// along a path to the end of the function.
1008+
if (livenessUpInst == term && isInOut) {
1009+
LLVM_DEBUG(llvm::dbgs()
1010+
<< " Found liveness inout error: " << inst);
1011+
// Even though we emit a diagnostic for inout here, we actually
1012+
// want to no longer track the inout liveness use and instead want
1013+
// to track the consuming use so that earlier errors are on the
1014+
// take and not on the inout. This is to ensure that we only emit
1015+
// a single inout not reinitialized before end of function error
1016+
// if we have multiple consumes along that path.
1017+
diagnosticEmitter.emitInOutEndOfFunctionDiagnostic(markedAddress,
1018+
&inst);
1019+
state.trackTakeForLivenessError(iter->first, iter->second);
1020+
} else {
1021+
// Otherwise, we just emit a normal liveness error.
1022+
LLVM_DEBUG(llvm::dbgs() << " Found liveness error: " << inst);
1023+
diagnosticEmitter.emitAddressDiagnostic(markedAddress,
1024+
livenessUpInst, &inst,
1025+
false /*is not consuming*/);
1026+
state.trackTakeForLivenessError(iter->first, iter->second);
1027+
}
1028+
emittedSomeDiagnostic = true;
1029+
}
1030+
1031+
(void)emittedSomeDiagnostic;
1032+
assert(emittedSomeDiagnostic);
1033+
continue;
1034+
}
1035+
}
1036+
9591037
{
9601038
auto iter = addressUseState.livenessUses.find(&inst);
9611039
if (iter != addressUseState.livenessUses.end()) {
@@ -1424,27 +1502,24 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
14241502
addressUseState.reinitInsts.count(&*ii))
14251503
break;
14261504

1427-
if (addressUseState.takeInsts.count(&*ii)) {
1505+
assert(!addressUseState.takeInsts.count(&*ii) &&
1506+
"Double destroy?! Should have errored on this?!");
1507+
1508+
if (addressUseState.copyInsts.count(&*ii)) {
14281509
if (auto *li = dyn_cast<LoadInst>(&*ii)) {
1429-
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) {
1430-
// If we have a copy in the single block case, erase the destroy
1431-
// and visit the next one.
1432-
destroy->eraseFromParent();
1433-
changed = true;
1434-
foundSingleBlockCase = true;
1435-
break;
1436-
}
1437-
llvm_unreachable("Error?! This is a double destroy?!");
1510+
// If we have a copy in the single block case, erase the destroy
1511+
// and visit the next one.
1512+
destroy->eraseFromParent();
1513+
changed = true;
1514+
foundSingleBlockCase = true;
1515+
break;
14381516
}
14391517

14401518
if (auto *copyAddr = dyn_cast<CopyAddrInst>(&*ii)) {
1441-
if (!copyAddr->isTakeOfSrc()) {
1442-
destroy->eraseFromParent();
1443-
changed = true;
1444-
foundSingleBlockCase = true;
1445-
break;
1446-
}
1447-
llvm_unreachable("Error?! This is a double destroy?!");
1519+
destroy->eraseFromParent();
1520+
changed = true;
1521+
foundSingleBlockCase = true;
1522+
break;
14481523
}
14491524
}
14501525

@@ -1463,36 +1538,25 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
14631538
// Now that we have rewritten all borrows, convert any takes that are today
14641539
// copies into their take form. If we couldn't do this, we would have had an
14651540
// error.
1466-
for (auto take : addressUseState.takeInsts) {
1541+
for (auto take : addressUseState.copyInsts) {
14671542
if (auto *li = dyn_cast<LoadInst>(take.first)) {
1468-
switch (li->getOwnershipQualifier()) {
1469-
case LoadOwnershipQualifier::Unqualified:
1470-
case LoadOwnershipQualifier::Trivial:
1471-
llvm_unreachable("Should never hit this");
1472-
case LoadOwnershipQualifier::Take:
1473-
// This is already correct.
1474-
continue;
1475-
case LoadOwnershipQualifier::Copy:
1476-
// Convert this to its take form.
1477-
if (auto *access = dyn_cast<BeginAccessInst>(li->getOperand()))
1478-
access->setAccessKind(SILAccessKind::Modify);
1479-
li->setOwnershipQualifier(LoadOwnershipQualifier::Take);
1480-
changed = true;
1481-
continue;
1482-
}
1543+
// Convert this to its take form.
1544+
if (auto *access = dyn_cast<BeginAccessInst>(li->getOperand()))
1545+
access->setAccessKind(SILAccessKind::Modify);
1546+
li->setOwnershipQualifier(LoadOwnershipQualifier::Take);
1547+
changed = true;
1548+
continue;
14831549
}
14841550

14851551
if (auto *copy = dyn_cast<CopyAddrInst>(take.first)) {
1486-
if (copy->isTakeOfSrc())
1487-
continue;
14881552
// Convert this to its take form.
14891553
if (auto *access = dyn_cast<BeginAccessInst>(copy->getSrc()))
14901554
access->setAccessKind(SILAccessKind::Modify);
14911555
copy->setIsTakeOfSrc(IsTake);
14921556
continue;
14931557
}
14941558

1495-
llvm::dbgs() << "Take User: " << *take.first;
1559+
llvm::dbgs() << "Unhandled copy user: " << *take.first;
14961560
llvm_unreachable("Unhandled case?!");
14971561
}
14981562

0 commit comments

Comments
 (0)