Skip to content

Commit 9cf008c

Browse files
authored
Merge pull request #63337 from gottesmm/pr-d2869554cff24b4059130c3f6192a1417ed9b9cb
[move-only] When performing borrow to destructure transform and failing, make sure we clean up appropriately.
2 parents 48c6f59 + 48546fd commit 9cf008c

File tree

7 files changed

+390
-133
lines changed

7 files changed

+390
-133
lines changed

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ target_sources(swiftSILOptimizer PRIVATE
2626
MovedAsyncVarDebugInfoPropagator.cpp
2727
MoveOnlyAddressChecker.cpp
2828
MoveOnlyBorrowToDestructureTransform.cpp
29+
MoveOnlyBorrowToDestructureTransformTester.cpp
2930
MoveOnlyDeinitInsertion.cpp
3031
MoveOnlyDiagnostics.cpp
3132
MoveOnlyObjectChecker.cpp

lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp

Lines changed: 15 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
///
13-
/// \file This is a pass that converts the borrow + gep pattern to destructures
14-
/// or emits an error if it cannot be done. It is assumed that it runs
15-
/// immediately before move checking of objects runs. This ensures that the move
16-
/// checker does not need to worry about this problem and instead can just check
17-
/// that the newly inserted destructures do not cause move only errors.
13+
/// \file This is a transform that converts the borrow + gep pattern to
14+
/// destructures or emits an error if it cannot be done. It is assumed that it
15+
/// runs immediately before move checking of objects runs. This ensures that the
16+
/// move checker does not need to worry about this problem and instead can just
17+
/// check that the newly inserted destructures do not cause move only errors.
18+
///
19+
/// This is written as a utility so that we can have a utility pass that tests
20+
/// this directly but also invoke this via the move only object checker.
21+
///
22+
/// TODO: Move this to SILOptimizer/Utils.
1823
///
1924
//===----------------------------------------------------------------------===//
2025

@@ -28,6 +33,8 @@
2833
#include "swift/SIL/SILInstruction.h"
2934
#include "swift/SILOptimizer/Analysis/Analysis.h"
3035
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
36+
#include "swift/Basic/BlotSetVector.h"
37+
#include "swift/Basic/FrozenMultiMap.h"
3138
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
3239
#include "swift/SILOptimizer/PassManager/Passes.h"
3340
#include "swift/SILOptimizer/PassManager/Transforms.h"
@@ -37,9 +44,6 @@
3744
using namespace swift;
3845
using namespace swift::siloptimizer;
3946

40-
using namespace swift;
41-
using namespace swift::siloptimizer;
42-
4347
namespace {
4448
using AvailableValues = BorrowToDestructureTransform::AvailableValues;
4549
}
@@ -202,8 +206,8 @@ void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const {
202206
for (auto *use : destructureNeedingUses) {
203207
LLVM_DEBUG(llvm::dbgs()
204208
<< " DestructureNeedingUse: " << *use->getUser());
205-
auto destructureUse = *TypeTreeLeafTypeRange::get(use->get(), mmci);
206-
if (liveness.isWithinBoundary(use->getUser(), destructureUse)) {
209+
auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), mmci);
210+
if (liveness.isWithinBoundary(use->getUser(), destructureUseSpan)) {
207211
LLVM_DEBUG(llvm::dbgs() << " Within boundary! Emitting error!\n");
208212
// Emit an error. We have a use after free.
209213
//
@@ -217,7 +221,7 @@ void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const {
217221
liveness.getNumSubElements());
218222
liveness.computeBoundary(boundary);
219223
diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary(
220-
mmci, use->getUser(), destructureUse, boundary);
224+
mmci, use->getUser(), destructureUseSpan, boundary);
221225
return;
222226
} else {
223227
LLVM_DEBUG(llvm::dbgs() << " On boundary! No error!\n");
@@ -1323,115 +1327,3 @@ void BorrowToDestructureTransform::cleanup(
13231327
// And finally do the same thing for our initial copy_value.
13241328
addCompensatingDestroys(liveness, boundary, initialValue);
13251329
}
1326-
1327-
//===----------------------------------------------------------------------===//
1328-
// Top Level Entrypoint
1329-
//===----------------------------------------------------------------------===//
1330-
1331-
static bool runTransform(SILFunction *fn,
1332-
ArrayRef<MarkMustCheckInst *> moveIntroducersToProcess,
1333-
PostOrderAnalysis *poa,
1334-
DiagnosticEmitter &diagnosticEmitter) {
1335-
BorrowToDestructureTransform::IntervalMapAllocator allocator;
1336-
bool madeChange = false;
1337-
while (!moveIntroducersToProcess.empty()) {
1338-
auto *mmci = moveIntroducersToProcess.back();
1339-
moveIntroducersToProcess = moveIntroducersToProcess.drop_back();
1340-
1341-
StackList<BeginBorrowInst *> borrowWorklist(mmci->getFunction());
1342-
1343-
// If we failed to gather borrows due to the transform not understanding
1344-
// part of the SIL, fail and return false.
1345-
if (!BorrowToDestructureTransform::gatherBorrows(mmci, borrowWorklist))
1346-
return madeChange;
1347-
1348-
// If we do not have any borrows to process, continue and process the next
1349-
// instruction.
1350-
if (borrowWorklist.empty())
1351-
continue;
1352-
1353-
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
1354-
1355-
// Now that we have found all of our borrows, we want to find struct_extract
1356-
// uses of our borrow as well as any operands that cannot use an owned
1357-
// value.
1358-
SWIFT_DEFER { discoveredBlocks.clear(); };
1359-
BorrowToDestructureTransform transform(allocator, mmci, diagnosticEmitter,
1360-
poa, discoveredBlocks);
1361-
1362-
// Attempt to gather uses. Return if we saw something that we did not
1363-
// understand. Return made change so we invalidate as appropriate.
1364-
if (!transform.gatherUses(borrowWorklist))
1365-
return madeChange;
1366-
1367-
// Next make sure that any destructure needing instructions are on the
1368-
// boundary in a per bit field sensitive manner.
1369-
transform.checkDestructureUsesOnBoundary();
1370-
1371-
// If we emitted any diagnostic, break out. We return true since we actually
1372-
// succeeded in our processing by finding the error. We only return false if
1373-
// we want to tell the rest of the checker that there was an internal
1374-
// compiler error that we need to emit a "compiler doesn't understand
1375-
// error".
1376-
if (diagnosticEmitter.emittedAnyDiagnostics())
1377-
return madeChange;
1378-
1379-
// At this point, we know that all of our destructure requiring uses are on
1380-
// the boundary of our live range. Now we need to do the rewriting.
1381-
transform.blockToAvailableValues.emplace(transform.liveness);
1382-
transform.rewriteUses();
1383-
1384-
// Now that we have done our rewritting, we need to do a few cleanups.
1385-
transform.cleanup(borrowWorklist);
1386-
}
1387-
1388-
return madeChange;
1389-
}
1390-
1391-
namespace {
1392-
1393-
class MoveOnlyBorrowToDestructureTransformPass : public SILFunctionTransform {
1394-
void run() override {
1395-
auto *fn = getFunction();
1396-
1397-
// Only run this pass if the move only language feature is enabled.
1398-
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
1399-
return;
1400-
1401-
// Don't rerun diagnostics on deserialized functions.
1402-
if (getFunction()->wasDeserializedCanonical())
1403-
return;
1404-
1405-
assert(fn->getModule().getStage() == SILStage::Raw &&
1406-
"Should only run on Raw SIL");
1407-
1408-
LLVM_DEBUG(llvm::dbgs() << "===> MoveOnly Object Checker. Visiting: "
1409-
<< fn->getName() << '\n');
1410-
1411-
auto *postOrderAnalysis = getAnalysis<PostOrderAnalysis>();
1412-
1413-
SmallSetVector<MarkMustCheckInst *, 32> moveIntroducersToProcess;
1414-
DiagnosticEmitter emitter;
1415-
1416-
bool madeChange = searchForCandidateObjectMarkMustChecks(
1417-
getFunction(), moveIntroducersToProcess, emitter);
1418-
if (madeChange) {
1419-
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
1420-
}
1421-
1422-
if (emitter.emittedAnyDiagnostics())
1423-
return;
1424-
1425-
auto introducers = llvm::makeArrayRef(moveIntroducersToProcess.begin(),
1426-
moveIntroducersToProcess.end());
1427-
if (runTransform(fn, introducers, postOrderAnalysis, emitter)) {
1428-
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
1429-
}
1430-
}
1431-
};
1432-
1433-
} // namespace
1434-
1435-
SILTransform *swift::createMoveOnlyBorrowToDestructureTransform() {
1436-
return new MoveOnlyBorrowToDestructureTransformPass();
1437-
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//===--- MoveOnlyBorrowToDestructureTransform.cpp -------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// \file This is a pass that converts the borrow + gep pattern to destructures
14+
/// or emits an error if it cannot be done. It is assumed that it runs
15+
/// immediately before move checking of objects runs. This ensures that the move
16+
/// checker does not need to worry about this problem and instead can just check
17+
/// that the newly inserted destructures do not cause move only errors.
18+
///
19+
//===----------------------------------------------------------------------===//
20+
21+
#define DEBUG_TYPE "sil-move-only-checker"
22+
23+
#include "MoveOnlyDiagnostics.h"
24+
#include "MoveOnlyObjectChecker.h"
25+
26+
#include "swift/Basic/Defer.h"
27+
#include "swift/SIL/SILBuilder.h"
28+
#include "swift/SIL/SILInstruction.h"
29+
#include "swift/SILOptimizer/Analysis/Analysis.h"
30+
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
31+
#include "swift/Basic/BlotSetVector.h"
32+
#include "swift/Basic/FrozenMultiMap.h"
33+
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
34+
#include "swift/SILOptimizer/PassManager/Passes.h"
35+
#include "swift/SILOptimizer/PassManager/Transforms.h"
36+
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
37+
#include "llvm/ADT/ArrayRef.h"
38+
39+
using namespace swift;
40+
using namespace swift::siloptimizer;
41+
42+
//===----------------------------------------------------------------------===//
43+
// Top Level Entrypoint
44+
//===----------------------------------------------------------------------===//
45+
46+
static bool runTransform(SILFunction *fn,
47+
ArrayRef<MarkMustCheckInst *> moveIntroducersToProcess,
48+
PostOrderAnalysis *poa,
49+
DiagnosticEmitter &diagnosticEmitter) {
50+
BorrowToDestructureTransform::IntervalMapAllocator allocator;
51+
bool madeChange = false;
52+
while (!moveIntroducersToProcess.empty()) {
53+
auto *mmci = moveIntroducersToProcess.back();
54+
moveIntroducersToProcess = moveIntroducersToProcess.drop_back();
55+
56+
unsigned currentDiagnosticCount = diagnosticEmitter.getDiagnosticCount();
57+
58+
StackList<BeginBorrowInst *> borrowWorklist(mmci->getFunction());
59+
60+
// If we failed to gather borrows due to the transform not understanding
61+
// part of the SIL, emit a diagnostic, RAUW the mark must check, and
62+
// continue.
63+
if (!BorrowToDestructureTransform::gatherBorrows(mmci, borrowWorklist)) {
64+
diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci);
65+
mmci->replaceAllUsesWith(mmci->getOperand());
66+
mmci->eraseFromParent();
67+
madeChange = true;
68+
continue;
69+
}
70+
71+
// If we do not have any borrows to process, continue and process the next
72+
// instruction.
73+
if (borrowWorklist.empty())
74+
continue;
75+
76+
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
77+
78+
// Now that we have found all of our borrows, we want to find struct_extract
79+
// uses of our borrow as well as any operands that cannot use an owned
80+
// value.
81+
SWIFT_DEFER { discoveredBlocks.clear(); };
82+
BorrowToDestructureTransform transform(allocator, mmci, diagnosticEmitter,
83+
poa, discoveredBlocks);
84+
85+
// Attempt to gather uses. Return if we saw something that we did not
86+
// understand. Emit a compiler did not understand diagnostic, RAUW the mmci
87+
// so later passes do not see it, and set madeChange to true.
88+
if (!transform.gatherUses(borrowWorklist)) {
89+
diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci);
90+
mmci->replaceAllUsesWith(mmci->getOperand());
91+
mmci->eraseFromParent();
92+
madeChange = true;
93+
continue;
94+
}
95+
96+
// Next make sure that any destructure needing instructions are on the
97+
// boundary in a per bit field sensitive manner.
98+
transform.checkDestructureUsesOnBoundary();
99+
100+
// If we emitted any diagnostic, set madeChange to true, eliminate our mmci,
101+
// and continue.
102+
if (currentDiagnosticCount != diagnosticEmitter.getDiagnosticCount()) {
103+
mmci->replaceAllUsesWith(mmci->getOperand());
104+
mmci->eraseFromParent();
105+
madeChange = true;
106+
continue;
107+
}
108+
109+
// At this point, we know that all of our destructure requiring uses are on
110+
// the boundary of our live range. Now we need to do the rewriting.
111+
transform.blockToAvailableValues.emplace(transform.liveness);
112+
transform.rewriteUses();
113+
114+
// Now that we have done our rewritting, we need to do a few cleanups.
115+
//
116+
// NOTE: We do not eliminate our mark_must_check since we want later passes
117+
// to do additional checking upon the mark_must_check including making sure
118+
// that our destructures do not need cause the need for additional copies to
119+
// be inserted. We only eliminate the mark_must_check if we emitted a
120+
// diagnostic of some sort.
121+
transform.cleanup(borrowWorklist);
122+
madeChange = true;
123+
}
124+
125+
return madeChange;
126+
}
127+
128+
namespace {
129+
130+
class MoveOnlyBorrowToDestructureTransformPass : public SILFunctionTransform {
131+
void run() override {
132+
auto *fn = getFunction();
133+
134+
// Only run this pass if the move only language feature is enabled.
135+
if (!fn->getASTContext().LangOpts.Features.contains(Feature::MoveOnly))
136+
return;
137+
138+
// Don't rerun diagnostics on deserialized functions.
139+
if (getFunction()->wasDeserializedCanonical())
140+
return;
141+
142+
assert(fn->getModule().getStage() == SILStage::Raw &&
143+
"Should only run on Raw SIL");
144+
145+
LLVM_DEBUG(llvm::dbgs()
146+
<< "===> MoveOnlyBorrowToDestructureTransform. Visiting: "
147+
<< fn->getName() << '\n');
148+
149+
auto *postOrderAnalysis = getAnalysis<PostOrderAnalysis>();
150+
151+
SmallSetVector<MarkMustCheckInst *, 32> moveIntroducersToProcess;
152+
DiagnosticEmitter emitter;
153+
154+
bool madeChange = searchForCandidateObjectMarkMustChecks(
155+
getFunction(), moveIntroducersToProcess, emitter);
156+
if (madeChange) {
157+
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
158+
}
159+
160+
if (emitter.emittedAnyDiagnostics()) {
161+
if (cleanupSILAfterEmittingObjectMoveOnlyDiagnostics(fn))
162+
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
163+
return;
164+
}
165+
166+
auto introducers = llvm::makeArrayRef(moveIntroducersToProcess.begin(),
167+
moveIntroducersToProcess.end());
168+
if (runTransform(fn, introducers, postOrderAnalysis, emitter)) {
169+
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
170+
}
171+
172+
if (emitter.emittedAnyDiagnostics()) {
173+
if (cleanupSILAfterEmittingObjectMoveOnlyDiagnostics(fn))
174+
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
175+
}
176+
}
177+
};
178+
179+
} // namespace
180+
181+
SILTransform *swift::createMoveOnlyBorrowToDestructureTransform() {
182+
return new MoveOnlyBorrowToDestructureTransformPass();
183+
}

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,13 @@ class DiagnosticEmitter {
4141
/// here.
4242
SmallPtrSet<MarkMustCheckInst *, 4> valuesWithDiagnostics;
4343

44-
// Track any violating uses we have emitted a diagnostic for so we don't emit
45-
// multiple diagnostics for the same use.
44+
/// Track any violating uses we have emitted a diagnostic for so we don't emit
45+
/// multiple diagnostics for the same use.
4646
SmallPtrSet<SILInstruction *, 8> useWithDiagnostic;
4747

48+
/// A count of the total diagnostics emitted so that callers of routines that
49+
/// take a diagnostic emitter can know if the emitter emitted additional
50+
/// diagnosics while running a callee.
4851
unsigned diagnosticCount = 0;
4952

5053
public:

0 commit comments

Comments
 (0)