@@ -250,24 +250,32 @@ void MoveKillsCopyableValuesChecker::emitDiagnosticForMove(
250
250
251
251
// Then we do a bit of work to figure out where /all/ of the later uses than
252
252
// mvi are so we can emit notes to the user telling them this is a problem
253
- // use. We can do a little more work here since we are going to be emitting a
254
- // fatalError ending the program.
253
+ // use. We can do a little more work here since we already know that we are
254
+ // going to be emitting a diagnostic and thus later parts of the compiler are
255
+ // not going to run. First we look for uses in the same block as our move.
255
256
auto *mviBlock = mvi->getParent ();
256
257
auto mviBlockLiveness = livenessInfo.liveness .getBlockLiveness (mviBlock);
257
258
switch (mviBlockLiveness) {
258
259
case PrunedLiveBlocks::Dead:
259
260
llvm_unreachable (" We should never see this" );
260
261
case PrunedLiveBlocks::LiveWithin: {
261
262
// The boundary was within our block. We need to search for uses later than
262
- // us and emit a diagnostic upon them. Then we return. We leave the rest of
263
+ // us and emit a diagnostic upon them and then return. We leave the rest of
263
264
// the function for the implementation of the LiveOutCase.
265
+ //
266
+ // NOTE: This does mean that once the user fixes this use, they will get
267
+ // additional errors that we did not diagnose before. We do this to simplify
268
+ // the implementation noting that the program in either case will not
269
+ // compile meaning correctness will be maintained despite this
270
+ // implementation choice.
264
271
for (SILInstruction &inst :
265
272
make_range (std::next (mvi->getIterator ()), mviBlock->end ())) {
266
273
switch (livenessInfo.liveness .isInterestingUser (&inst)) {
267
274
case PrunedLiveness::NonUser:
268
275
break ;
269
276
case PrunedLiveness::NonLifetimeEndingUse:
270
277
case PrunedLiveness::LifetimeEndingUse:
278
+ LLVM_DEBUG (llvm::dbgs () << " Emitting note for in block use: " << inst);
271
279
diagnose (astContext, inst.getLoc ().getSourceLoc (),
272
280
diag::sil_movekillscopyablevalue_use_here);
273
281
break ;
@@ -287,73 +295,97 @@ void MoveKillsCopyableValuesChecker::emitDiagnosticForMove(
287
295
" We are handling only the live out case here. The rest of the cases "
288
296
" were handled in the switch above and return early upon success" );
289
297
298
+ // Ok, our boundary was later, so we need to search the CFG along successor
299
+ // edges starting at the successors's of our move function block
290
300
BasicBlockWorklist worklist (mvi->getFunction ());
291
301
for (auto *succBlock : mvi->getParent ()->getSuccessorBlocks ()) {
292
302
worklist.pushIfNotVisited (succBlock);
293
303
}
294
304
295
305
// In order to make sure that we do not miss uses that are within loops, we
296
- // maintain a list of all user sets. The issue is that a block at a deeper
297
- // loop level than our def, even if it contained the use that triggered the
298
- // issue will be LiveOut. So when we see a live out block, we perform this
299
- // extra check and emit a diagnostic if needed.
306
+ // maintain a list of all user sets.
307
+ //
308
+ // DISCUSSION: The issue is that a block at a deeper loop level than our def,
309
+ // even if it contained the use that triggered the issue will be LiveOut. So
310
+ // when we see a live out block, we perform this extra check and emit a
311
+ // diagnostic if needed.
300
312
BasicBlockSet usesToCheckForInLiveOutBlocks (mvi->getFunction ());
301
313
for (auto *user : livenessInfo.nonLifetimeEndingUsesInLiveOut )
302
314
usesToCheckForInLiveOutBlocks.insert (user->getParent ());
303
- for (auto *consumingUse : livenessInfo.consumingUse )
304
- usesToCheckForInLiveOutBlocks.insert (consumingUse->getParentBlock ());
315
+ for (auto *consumingUse : livenessInfo.consumingUse ) {
316
+ // We ignore consuming uses that are destroy_value since in our model they
317
+ // do not provide liveness.
318
+ if (!isa<DestroyValueInst>(consumingUse->getUser ()))
319
+ usesToCheckForInLiveOutBlocks.insert (consumingUse->getParentBlock ());
320
+ }
305
321
306
322
while (auto *block = worklist.pop ()) {
307
- if (PrunedLiveBlocks::LiveOut ==
323
+ // First do a quick check if we are not a live out block. If so, the
324
+ // boundary was within the block. We need to search for interesting uses in
325
+ // the block and then emit diagnostics upon them. We then continue without
326
+ // adding successors since we do not need to look further than the pruned
327
+ // liveness boundary for uses.
328
+ if (PrunedLiveBlocks::LiveOut !=
308
329
livenessInfo.liveness .getBlockLiveness (block)) {
309
- // Make sure that if we have a liveout block that is at a lower level in
310
- // the loop nest than our def and we have a use in that block, that we
311
- // emit an error. We know it is after the move since we are visiting
312
- // instructions in successors of move.
313
- if (usesToCheckForInLiveOutBlocks.contains (block)) {
314
- for (SILInstruction &inst : *block) {
315
- if (livenessInfo.nonLifetimeEndingUsesInLiveOut .contains (&inst)) {
316
- diagnose (astContext, inst.getLoc ().getSourceLoc (),
317
- diag::sil_movekillscopyablevalue_use_here);
318
- continue ;
319
- }
320
- for (auto &op : inst.getAllOperands ()) {
321
- if (livenessInfo.consumingUse .contains (&op)) {
322
- // If one of our in loop moves is ourselves, then we know that our
323
- // original value is outside of the loop and thus we have a loop
324
- // carry dataflow violation.
325
- if (mvi == &inst) {
326
- diagnose (
327
- astContext, inst.getLoc ().getSourceLoc (),
328
- diag::sil_movekillscopyablevalue_value_consumed_in_loop);
329
- continue ;
330
- }
331
-
332
- diagnose (astContext, inst.getLoc ().getSourceLoc (),
333
- diag::sil_movekillscopyablevalue_use_here);
334
- continue ;
335
- }
336
- }
330
+ for (SILInstruction &inst : *block) {
331
+ switch (livenessInfo.liveness .isInterestingUser (&inst)) {
332
+ case PrunedLiveness::NonUser:
333
+ break ;
334
+ case PrunedLiveness::NonLifetimeEndingUse:
335
+ case PrunedLiveness::LifetimeEndingUse:
336
+ LLVM_DEBUG (llvm::dbgs ()
337
+ << " (3) Emitting diagnostic for user: " << inst);
338
+ diagnose (astContext, inst.getLoc ().getSourceLoc (),
339
+ diag::sil_movekillscopyablevalue_use_here);
340
+ break ;
337
341
}
338
342
}
339
-
340
- for (auto *succBlock : block->getSuccessorBlocks ()) {
341
- worklist.pushIfNotVisited (succBlock);
342
- }
343
343
continue ;
344
344
}
345
345
346
- // The boundary was within the block. We need to search for interesting uses
347
- // in the block and then emit diagnostics upon them.
346
+ // Otherwise, we have a live out block. First before we do anything, add the
347
+ // successors of this block to the worklist.
348
+ for (auto *succBlock : block->getSuccessorBlocks ())
349
+ worklist.pushIfNotVisited (succBlock);
350
+
351
+ // Then check if we have any of those deeper loop nest uses. If not, we are
352
+ // done with this block and continue...
353
+ if (!usesToCheckForInLiveOutBlocks.contains (block))
354
+ continue ;
355
+
356
+ // Ok! This is a live out block with a use we need to emit an error for . We
357
+ // know it is reachable from the move since we are walking successors from
358
+ // the move block. Of course, if we do not have any such uses... just
359
+ // continue.
348
360
for (SILInstruction &inst : *block) {
349
- switch (livenessInfo.liveness .isInterestingUser (&inst)) {
350
- case PrunedLiveness::NonUser:
351
- break ;
352
- case PrunedLiveness::NonLifetimeEndingUse:
353
- case PrunedLiveness::LifetimeEndingUse:
361
+ if (livenessInfo.nonLifetimeEndingUsesInLiveOut .contains (&inst)) {
362
+ LLVM_DEBUG (llvm::dbgs ()
363
+ << " (1) Emitting diagnostic for user: " << inst);
354
364
diagnose (astContext, inst.getLoc ().getSourceLoc (),
355
365
diag::sil_movekillscopyablevalue_use_here);
356
- break ;
366
+ continue ;
367
+ }
368
+
369
+ for (auto &op : inst.getAllOperands ()) {
370
+ if (livenessInfo.consumingUse .contains (&op)) {
371
+ // If one of our in loop moves is ourselves, then we know that our
372
+ // original value is outside of the loop and thus we have a loop
373
+ // carry dataflow violation.
374
+ if (mvi == &inst) {
375
+ diagnose (astContext, inst.getLoc ().getSourceLoc (),
376
+ diag::sil_movekillscopyablevalue_value_consumed_in_loop);
377
+ continue ;
378
+ }
379
+ // We ignore consuming uses that are destroy_value since in our model
380
+ // they do not provide liveness.
381
+ if (isa<DestroyValueInst>(inst))
382
+ continue ;
383
+
384
+ LLVM_DEBUG (llvm::dbgs ()
385
+ << " (2) Emitting diagnostic for user: " << inst);
386
+ diagnose (astContext, inst.getLoc ().getSourceLoc (),
387
+ diag::sil_movekillscopyablevalue_use_here);
388
+ }
357
389
}
358
390
}
359
391
}
0 commit comments