28
28
#include " MoveOnlyDiagnostics.h"
29
29
#include " MoveOnlyObjectChecker.h"
30
30
31
+ #include " swift/Basic/BlotSetVector.h"
31
32
#include " swift/Basic/Defer.h"
33
+ #include " swift/Basic/FrozenMultiMap.h"
34
+ #include " swift/SIL/FieldSensitivePrunedLiveness.h"
32
35
#include " swift/SIL/SILBuilder.h"
33
36
#include " swift/SIL/SILInstruction.h"
34
37
#include " swift/SILOptimizer/Analysis/Analysis.h"
35
38
#include " swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
36
- #include " swift/Basic/BlotSetVector.h"
37
- #include " swift/Basic/FrozenMultiMap.h"
38
39
#include " swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
39
40
#include " swift/SILOptimizer/PassManager/Passes.h"
40
41
#include " swift/SILOptimizer/PassManager/Transforms.h"
41
42
#include " swift/SILOptimizer/Utils/CFGOptUtils.h"
42
43
#include " llvm/ADT/ArrayRef.h"
44
+ #include " llvm/ADT/SmallBitVector.h"
43
45
44
46
using namespace swift ;
45
47
using namespace swift ::siloptimizer;
@@ -143,6 +145,7 @@ bool BorrowToDestructureTransform::gatherUses(
143
145
{nextUse, {*leafRange, false /* is lifetime ending*/ }});
144
146
liveness.updateForUse (nextUse->getUser (), *leafRange,
145
147
false /* is lifetime ending*/ );
148
+ instToInterestingOperandIndexMap.insert (nextUse->getUser (), nextUse);
146
149
continue ;
147
150
}
148
151
@@ -166,6 +169,7 @@ bool BorrowToDestructureTransform::gatherUses(
166
169
{nextUse, {*leafRange, true /* is lifetime ending*/ }});
167
170
liveness.updateForUse (nextUse->getUser (), *leafRange,
168
171
true /* is lifetime ending*/ );
172
+ instToInterestingOperandIndexMap.insert (nextUse->getUser (), nextUse);
169
173
continue ;
170
174
}
171
175
@@ -198,39 +202,163 @@ bool BorrowToDestructureTransform::gatherUses(
198
202
return true ;
199
203
}
200
204
205
+ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction () {
206
+ // At this point, we have emitted all boundary checks. We also now need to
207
+ // check if any of our consuming uses that are on the boundary are used by the
208
+ // same instruction as a different consuming or non-consuming use.
209
+ instToInterestingOperandIndexMap.setFrozen ();
210
+ SmallBitVector usedBits (liveness.getNumSubElements ());
211
+
212
+ for (auto instRangePair : instToInterestingOperandIndexMap.getRange ()) {
213
+ SWIFT_DEFER { usedBits.reset (); };
214
+
215
+ // First loop through our uses and handle any consuming twice errors. We
216
+ // also setup usedBits to check for non-consuming uses that may overlap.
217
+ Operand *badOperand = nullptr ;
218
+ Optional<TypeTreeLeafTypeRange> badRange;
219
+ for (auto *use : instRangePair.second ) {
220
+ if (!use->isConsuming ())
221
+ continue ;
222
+
223
+ auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
224
+ for (unsigned index : destructureUseSpan.getRange ()) {
225
+ if (usedBits[index]) {
226
+ // If we get that we used the same bit twice, we have an error. We set
227
+ // the badIndex error and break early.
228
+ badOperand = use;
229
+ badRange = destructureUseSpan;
230
+ break ;
231
+ }
232
+
233
+ usedBits[index] = true ;
234
+ }
235
+
236
+ // If we set badOperand, break so we can emit an error for this
237
+ // instruction.
238
+ if (badOperand)
239
+ break ;
240
+ }
241
+
242
+ // If we did not set badIndex for consuming uses, we did not have any
243
+ // conflicts among consuming uses. see if we have any conflicts with
244
+ // non-consuming uses. Otherwise, we continue.
245
+ if (!badOperand) {
246
+ for (auto *use : instRangePair.second ) {
247
+ if (use->isConsuming ())
248
+ continue ;
249
+
250
+ auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
251
+ for (unsigned index : destructureUseSpan.getRange ()) {
252
+ if (!usedBits[index])
253
+ continue ;
254
+
255
+ // If we get that we used the same bit twice, we have an error. We set
256
+ // the badIndex error and break early.
257
+ badOperand = use;
258
+ badRange = destructureUseSpan;
259
+ break ;
260
+ }
261
+
262
+ // If we set badOperand, break so we can emit an error for this
263
+ // instruction.
264
+ if (badOperand)
265
+ break ;
266
+ }
267
+
268
+ // If we even did not find a non-consuming use that conflicts, then
269
+ // continue.
270
+ if (!badOperand)
271
+ continue ;
272
+ }
273
+
274
+ // If badIndex is set, we broke out of the inner loop and need to emit an
275
+ // error. Use a little more compile time to identify the other operand that
276
+ // caused the failure. NOTE: badOperand /could/ be a non-consuming use, but
277
+ // the use we are identifying here will always be consuming.
278
+ usedBits.reset ();
279
+
280
+ // Reinitialize use bits with the bad bits.
281
+ for (unsigned index : badRange->getRange ())
282
+ usedBits[index] = true ;
283
+
284
+ // Now loop back through looking for the original operand that set the used
285
+ // bits. This will always be a consuming use.
286
+ for (auto *use : instRangePair.second ) {
287
+ if (!use->isConsuming ())
288
+ continue ;
289
+
290
+ auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
291
+ bool emittedError = false ;
292
+ for (unsigned index : destructureUseSpan.getRange ()) {
293
+ if (!usedBits[index])
294
+ continue ;
295
+
296
+ if (badOperand->isConsuming ())
297
+ diagnosticEmitter.emitObjectConsumesDestructuredValueTwice (
298
+ mmci, use, badOperand);
299
+ else
300
+ diagnosticEmitter.emitObjectConsumesAndUsesDestructuredValue (
301
+ mmci, use, badOperand);
302
+ emittedError = true ;
303
+ }
304
+
305
+ // Once we have emitted the error, just break out of the loop.
306
+ if (emittedError)
307
+ break ;
308
+ }
309
+ }
310
+ }
311
+
201
312
void BorrowToDestructureTransform::checkDestructureUsesOnBoundary () const {
202
313
LLVM_DEBUG (llvm::dbgs () << " Checking destructure uses on boundary!\n " );
314
+
203
315
// Now that we have found all of our destructure needing uses and liveness
204
316
// needing uses, make sure that none of our destructure needing uses are
205
- // within our boundary. If so, we have an automatic error.
317
+ // within our boundary. If so, we have an automatic error since we have a
318
+ // use-after-free.
206
319
for (auto *use : destructureNeedingUses) {
207
320
LLVM_DEBUG (llvm::dbgs ()
208
321
<< " DestructureNeedingUse: " << *use->getUser ());
322
+
209
323
auto destructureUseSpan = *TypeTreeLeafTypeRange::get (use->get (), mmci);
210
- if (liveness.isWithinBoundary (use->getUser (), destructureUseSpan)) {
211
- LLVM_DEBUG (llvm::dbgs () << " Within boundary! Emitting error!\n " );
212
- // Emit an error. We have a use after free.
213
- //
214
- // NOTE: Since we are going to emit an error here, we do the boundary
215
- // computation to ensure that we only do the boundary computation once:
216
- // when we emit an error or once we know we need to do rewriting.
217
- //
218
- // TODO: Fix diagnostic to use destructure needing use and boundary
219
- // uses.
220
- FieldSensitivePrunedLivenessBoundary boundary (
221
- liveness.getNumSubElements ());
222
- liveness.computeBoundary (boundary);
223
- diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary (
224
- mmci, use->getUser (), destructureUseSpan, boundary);
225
- return ;
226
- } else {
227
- LLVM_DEBUG (llvm::dbgs () << " On boundary! No error!\n " );
324
+ if (!liveness.isWithinBoundary (use->getUser (), destructureUseSpan)) {
325
+ LLVM_DEBUG (llvm::dbgs ()
326
+ << " On boundary or within boundary! No error!\n " );
327
+ continue ;
228
328
}
329
+
330
+ // Emit an error. We have a use after free.
331
+ //
332
+ // NOTE: Since we are going to emit an error here, we do the boundary
333
+ // computation to ensure that we only do the boundary computation once:
334
+ // when we emit an error or once we know we need to do rewriting.
335
+ //
336
+ // TODO: Fix diagnostic to use destructure needing use and boundary
337
+ // uses.
338
+ LLVM_DEBUG (llvm::dbgs () << " Within boundary! Emitting error!\n " );
339
+ FieldSensitivePrunedLivenessBoundary boundary (liveness.getNumSubElements ());
340
+ liveness.computeBoundary (boundary);
341
+ diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary (
342
+ mmci, use->getUser (), destructureUseSpan, boundary);
229
343
}
230
344
}
231
345
232
346
bool BorrowToDestructureTransform::gatherBorrows (
233
347
MarkMustCheckInst *mmci, StackList<BeginBorrowInst *> &borrowWorklist) {
348
+ // If we have a no implicit copy mark_must_check, we do not run the borrow to
349
+ // destructure transform since:
350
+ //
351
+ // 1. If we have a move only type, we should have emitted an earlier error
352
+ // saying that move only types should not be marked as no implicit copy.
353
+ //
354
+ // 2. If we do not have a move only type, then we know that all fields that we
355
+ // access directly and would cause a need to destructure must be copyable,
356
+ // so no transformation/error is needed.
357
+ if (mmci->getType ().isMoveOnlyWrapped ()) {
358
+ LLVM_DEBUG (llvm::dbgs () << " Skipping move only wrapped inst: " << *mmci);
359
+ return true ;
360
+ }
361
+
234
362
LLVM_DEBUG (llvm::dbgs () << " Searching for borrows for inst: " << *mmci);
235
363
236
364
StackList<Operand *> worklist (mmci->getFunction ());
0 commit comments