Skip to content

Commit 60aafef

Browse files
authored
Merge pull request #62168 from gottesmm/pr-361aa81d6d8a76244aebbad14d4342e76084e169
[move-only] Enable the move-only address transform and a small additional fix
2 parents ef74e0e + 89036b3 commit 60aafef

File tree

5 files changed

+177
-67
lines changed

5 files changed

+177
-67
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,8 @@ ERROR(sil_moveonlychecker_inout_not_reinitialized_before_end_of_function, none,
735735
"'%0' consumed but not reinitialized before end of function", (StringRef))
736736
ERROR(sil_moveonlychecker_value_consumed_in_a_loop, none,
737737
"'%0' consumed by a use in a loop", (StringRef))
738+
ERROR(sil_moveonlychecker_exclusivity_violation, none,
739+
"'%0' has consuming use that cannot be eliminated due to a tight exclusivity scope", (StringRef))
738740

739741
NOTE(sil_moveonlychecker_consuming_use_here, none,
740742
"consuming use", ())

lib/SILGen/SILGenExpr.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4758,7 +4758,19 @@ static void emitSimpleAssignment(SILGenFunction &SGF, SILLocation loc,
47584758
FormalEvaluationScope writeback(SGF);
47594759
auto srcLV = SGF.emitLValue(srcLoad->getSubExpr(),
47604760
SGFAccessKind::IgnoredRead);
4761-
(void) SGF.emitLoadOfLValue(loc, std::move(srcLV), SGFContext());
4761+
RValue rv = SGF.emitLoadOfLValue(loc, std::move(srcLV), SGFContext());
4762+
// If we have a move only type, we need to implode and perform a move to
4763+
// ensure we consume our argument as part of the assignment. Otherwise,
4764+
// we don't do anything.
4765+
if (rv.getLoweredType(SGF).isMoveOnly()) {
4766+
ManagedValue value = std::move(rv).getAsSingleValue(SGF, loc);
4767+
// If we have an address, then ensure plus one will create a temporary
4768+
// copy which will act as a consume of the address value. If we have
4769+
// an object, we need to insert our own move though.
4770+
value = value.ensurePlusOne(SGF, loc);
4771+
if (value.getType().isObject())
4772+
value = SGF.B.createMoveValue(loc, value);
4773+
}
47624774
return;
47634775
}
47644776

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 106 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
#include "llvm/ADT/PointerUnion.h"
147147
#include "llvm/ADT/SmallBitVector.h"
148148
#include "llvm/ADT/SmallPtrSet.h"
149+
#include "llvm/ADT/SmallVector.h"
149150
#include "llvm/Support/Debug.h"
150151
#include "llvm/Support/ErrorHandling.h"
151152

@@ -204,10 +205,31 @@ struct DiagnosticEmitter {
204205
SILInstruction *violatingUse);
205206
void emitAddressDiagnosticNoCopy(MarkMustCheckInst *markedValue,
206207
SILInstruction *consumingUse);
208+
void emitExclusivityHazardDiagnostic(MarkMustCheckInst *markedValue,
209+
SILInstruction *consumingUse);
207210
};
208211

209212
} // namespace
210213

214+
void DiagnosticEmitter::emitExclusivityHazardDiagnostic(
215+
MarkMustCheckInst *markedValue, SILInstruction *consumingUse) {
216+
if (!useWithDiagnostic.insert(consumingUse).second)
217+
return;
218+
219+
auto &astContext = markedValue->getFunction()->getASTContext();
220+
StringRef varName = getVariableNameForValue(markedValue);
221+
222+
LLVM_DEBUG(llvm::dbgs() << "Emitting error for exclusivity!\n");
223+
LLVM_DEBUG(llvm::dbgs() << " Mark: " << *markedValue);
224+
LLVM_DEBUG(llvm::dbgs() << " Consuming use: " << *consumingUse);
225+
226+
diagnose(astContext,
227+
markedValue->getDefiningInstruction()->getLoc().getSourceLoc(),
228+
diag::sil_moveonlychecker_exclusivity_violation, varName);
229+
diagnose(astContext, consumingUse->getLoc().getSourceLoc(),
230+
diag::sil_moveonlychecker_consuming_use_here);
231+
}
232+
211233
void DiagnosticEmitter::emitAddressDiagnostic(
212234
MarkMustCheckInst *markedValue, SILInstruction *lastLiveUse,
213235
SILInstruction *violatingUse, bool isUseConsuming,
@@ -775,13 +797,18 @@ struct GatherUsesVisitor : public AccessUseVisitor {
775797
bool emittedEarlyDiagnostic = false;
776798
DiagnosticEmitter &diagnosticEmitter;
777799

800+
// Pruned liveness used to validate that load [take]/load [copy] can be
801+
// converted to load_borrow without violating exclusivity.
802+
SSAPrunedLiveness &liveness;
803+
778804
GatherUsesVisitor(MoveOnlyChecker &moveChecker, UseState &useState,
779805
MarkMustCheckInst *markedValue,
780-
DiagnosticEmitter &diagnosticEmitter)
806+
DiagnosticEmitter &diagnosticEmitter,
807+
SSAPrunedLiveness &gatherUsesLiveness)
781808
: AccessUseVisitor(AccessUseType::Overlapping,
782809
NestedAccessType::IgnoreAccessBegin),
783810
moveChecker(moveChecker), useState(useState), markedValue(markedValue),
784-
diagnosticEmitter(diagnosticEmitter) {}
811+
diagnosticEmitter(diagnosticEmitter), liveness(gatherUsesLiveness) {}
785812

786813
bool visitUse(Operand *op, AccessUseType useTy) override;
787814
void reset(SILValue address) { useState.address = address; }
@@ -795,6 +822,41 @@ struct GatherUsesVisitor : public AccessUseVisitor {
795822
/// base address that we are checking which should be the operand of the mark
796823
/// must check value.
797824
SILValue getRootAddress() const { return markedValue; }
825+
826+
/// Returns true if we emitted an error.
827+
bool checkForExclusivityHazards(LoadInst *li) {
828+
SWIFT_DEFER { liveness.clear(); };
829+
830+
LLVM_DEBUG(llvm::dbgs() << "Checking for exclusivity hazards for: " << *li);
831+
832+
// Grab our access path with in scope. We want to find the inner most access
833+
// scope.
834+
auto accessPathWithBase =
835+
AccessPathWithBase::computeInScope(li->getOperand());
836+
auto accessPath = accessPathWithBase.accessPath;
837+
// TODO: Make this a we don't understand error.
838+
assert(accessPath.isValid() && "Invalid access path?!");
839+
840+
auto *bai = dyn_cast<BeginAccessInst>(accessPathWithBase.base);
841+
842+
if (!bai) {
843+
LLVM_DEBUG(llvm::dbgs()
844+
<< " No begin access... so no exclusivity violation!\n");
845+
return false;
846+
}
847+
848+
bool emittedError = false;
849+
liveness.initializeDef(bai);
850+
liveness.computeSimple();
851+
for (auto *consumingUse : li->getConsumingUses()) {
852+
if (!liveness.isWithinBoundary(consumingUse->getUser())) {
853+
diagnosticEmitter.emitExclusivityHazardDiagnostic(
854+
markedValue, consumingUse->getUser());
855+
emittedError = true;
856+
}
857+
}
858+
return emittedError;
859+
}
798860
};
799861

800862
} // end anonymous namespace
@@ -934,6 +996,14 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
934996
return false;
935997

936998
LLVM_DEBUG(llvm::dbgs() << "Found potential borrow: " << *user);
999+
1000+
if (checkForExclusivityHazards(li)) {
1001+
LLVM_DEBUG(llvm::dbgs() << "Found exclusivity violation?!\n");
1002+
emittedEarlyDiagnostic = true;
1003+
moveChecker.valuesWithDiagnostics.insert(markedValue);
1004+
return true;
1005+
}
1006+
9371007
useState.borrows.insert({user, *leafRange});
9381008
return true;
9391009
}
@@ -965,7 +1035,14 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
9651035
return false;
9661036

9671037
if (moveChecker.finalConsumingUses.empty()) {
968-
LLVM_DEBUG(llvm::dbgs() << "Found borrow inst: " << *user);
1038+
LLVM_DEBUG(llvm::dbgs() << "Found potential borrow inst: " << *user);
1039+
if (checkForExclusivityHazards(li)) {
1040+
LLVM_DEBUG(llvm::dbgs() << "Found exclusivity violation?!\n");
1041+
emittedEarlyDiagnostic = true;
1042+
moveChecker.valuesWithDiagnostics.insert(markedValue);
1043+
return true;
1044+
}
1045+
9691046
useState.borrows.insert({user, *leafRange});
9701047
} else {
9711048
LLVM_DEBUG(llvm::dbgs() << "Found take inst: " << *user);
@@ -1155,8 +1232,10 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
11551232
// Then gather all uses of our address by walking from def->uses. We use this
11561233
// to categorize the uses of this address into their ownership behavior (e.x.:
11571234
// init, reinit, take, destroy, etc.).
1235+
SmallVector<SILBasicBlock *, 32> gatherUsesDiscoveredBlocks;
1236+
SSAPrunedLiveness gatherUsesLiveness(&gatherUsesDiscoveredBlocks);
11581237
GatherUsesVisitor visitor(*this, addressUseState, markedAddress,
1159-
diagnosticEmitter);
1238+
diagnosticEmitter, gatherUsesLiveness);
11601239
SWIFT_DEFER { visitor.clear(); };
11611240
visitor.reset(markedAddress);
11621241
if (!visitAccessPathUses(visitor, accessPath, fn)) {
@@ -1467,13 +1546,6 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
14671546
}
14681547
}
14691548

1470-
// Now before we do anything more, just exit and say we errored. This will not
1471-
// actually make us fail and will cause code later in the checker to rewrite
1472-
// all non-explicit copy instructions to their explicit variant. I did this
1473-
// for testing purposes only.
1474-
valuesWithDiagnostics.insert(markedAddress);
1475-
return true;
1476-
14771549
// Ok, we not have emitted our main errors. Now we begin the
14781550
// transformation. We begin by processing borrows. We can also emit errors
14791551
// here if we find that we can not expand the borrow scope of the load [copy]
@@ -1487,50 +1559,16 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
14871559
// must have been destroy_value. So we can just gather up those
14881560
// destroy_value and use then to create a new load_borrow scope.
14891561
SILBuilderWithScope builder(li);
1490-
SILValue base = stripAccessMarkers(li->getOperand());
1491-
bool foundAccess = base != li->getOperand();
1492-
1493-
// Insert a new access scope.
1494-
SILValue newBase = base;
1495-
if (foundAccess) {
1496-
newBase = builder.createBeginAccess(
1497-
li->getLoc(), base, SILAccessKind::Read,
1498-
SILAccessEnforcement::Static, false /*no nested conflict*/,
1499-
false /*is from builtin*/);
1500-
}
1501-
auto *lbi = builder.createLoadBorrow(li->getLoc(), newBase);
1562+
auto *lbi = builder.createLoadBorrow(li->getLoc(), li->getOperand());
15021563

15031564
for (auto *consumeUse : li->getConsumingUses()) {
15041565
auto *dvi = cast<DestroyValueInst>(consumeUse->getUser());
15051566
SILBuilderWithScope destroyBuilder(dvi);
15061567
destroyBuilder.createEndBorrow(dvi->getLoc(), lbi);
1507-
if (foundAccess)
1508-
destroyBuilder.createEndAccess(dvi->getLoc(), newBase,
1509-
false /*aborted*/);
15101568
destroys.push_back(dvi);
15111569
changed = true;
15121570
}
15131571

1514-
// TODO: We need to check if this is the only use.
1515-
if (auto *access = dyn_cast<BeginAccessInst>(li->getOperand())) {
1516-
bool foundUnknownUse = false;
1517-
for (auto *accUse : access->getUses()) {
1518-
if (accUse->getUser() == li)
1519-
continue;
1520-
if (auto *ea = dyn_cast<EndAccessInst>(accUse->getUser())) {
1521-
endAccesses.push_back(ea);
1522-
continue;
1523-
}
1524-
foundUnknownUse = true;
1525-
}
1526-
if (!foundUnknownUse) {
1527-
for (auto *end : endAccesses)
1528-
end->eraseFromParent();
1529-
access->replaceAllUsesWithUndef();
1530-
access->eraseFromParent();
1531-
}
1532-
}
1533-
15341572
for (auto *d : destroys)
15351573
d->eraseFromParent();
15361574
li->replaceAllUsesWith(lbi);
@@ -1539,6 +1577,7 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
15391577
}
15401578
}
15411579

1580+
llvm::dbgs() << "Borrow: " << *pair.first;
15421581
llvm_unreachable("Unhandled case?!");
15431582
}
15441583

@@ -1570,6 +1609,16 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
15701609
}
15711610
llvm_unreachable("Error?! This is a double destroy?!");
15721611
}
1612+
1613+
if (auto *copyAddr = dyn_cast<CopyAddrInst>(&*ii)) {
1614+
if (!copyAddr->isTakeOfSrc()) {
1615+
destroy->eraseFromParent();
1616+
changed = true;
1617+
foundSingleBlockCase = true;
1618+
break;
1619+
}
1620+
llvm_unreachable("Error?! This is a double destroy?!");
1621+
}
15731622
}
15741623

15751624
if (addressUseState.livenessUses.count(&*ii)) {
@@ -1606,6 +1655,17 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
16061655
}
16071656
}
16081657

1658+
if (auto *copy = dyn_cast<CopyAddrInst>(take.first)) {
1659+
if (copy->isTakeOfSrc())
1660+
continue;
1661+
// Convert this to its take form.
1662+
if (auto *access = dyn_cast<BeginAccessInst>(copy->getSrc()))
1663+
access->setAccessKind(SILAccessKind::Modify);
1664+
copy->setIsTakeOfSrc(IsTake);
1665+
continue;
1666+
}
1667+
1668+
llvm::dbgs() << "Take User: " << *take.first;
16091669
llvm_unreachable("Unhandled case?!");
16101670
}
16111671

0 commit comments

Comments
 (0)