@@ -250,24 +250,32 @@ void MoveKillsCopyableValuesChecker::emitDiagnosticForMove(
250250
251251 // Then we do a bit of work to figure out where /all/ of the later uses than
252252 // 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.
255256 auto *mviBlock = mvi->getParent ();
256257 auto mviBlockLiveness = livenessInfo.liveness .getBlockLiveness (mviBlock);
257258 switch (mviBlockLiveness) {
258259 case PrunedLiveBlocks::Dead:
259260 llvm_unreachable (" We should never see this" );
260261 case PrunedLiveBlocks::LiveWithin: {
261262 // 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
263264 // 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.
264271 for (SILInstruction &inst :
265272 make_range (std::next (mvi->getIterator ()), mviBlock->end ())) {
266273 switch (livenessInfo.liveness .isInterestingUser (&inst)) {
267274 case PrunedLiveness::NonUser:
268275 break ;
269276 case PrunedLiveness::NonLifetimeEndingUse:
270277 case PrunedLiveness::LifetimeEndingUse:
278+ LLVM_DEBUG (llvm::dbgs () << " Emitting note for in block use: " << inst);
271279 diagnose (astContext, inst.getLoc ().getSourceLoc (),
272280 diag::sil_movekillscopyablevalue_use_here);
273281 break ;
@@ -287,73 +295,97 @@ void MoveKillsCopyableValuesChecker::emitDiagnosticForMove(
287295 " We are handling only the live out case here. The rest of the cases "
288296 " were handled in the switch above and return early upon success" );
289297
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
290300 BasicBlockWorklist worklist (mvi->getFunction ());
291301 for (auto *succBlock : mvi->getParent ()->getSuccessorBlocks ()) {
292302 worklist.pushIfNotVisited (succBlock);
293303 }
294304
295305 // 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.
300312 BasicBlockSet usesToCheckForInLiveOutBlocks (mvi->getFunction ());
301313 for (auto *user : livenessInfo.nonLifetimeEndingUsesInLiveOut )
302314 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+ }
305321
306322 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 !=
308329 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 ;
337341 }
338342 }
339-
340- for (auto *succBlock : block->getSuccessorBlocks ()) {
341- worklist.pushIfNotVisited (succBlock);
342- }
343343 continue ;
344344 }
345345
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.
348360 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);
354364 diagnose (astContext, inst.getLoc ().getSourceLoc (),
355365 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+ }
357389 }
358390 }
359391 }
0 commit comments