48
48
// / alternative would be to model all end_access instructions as deinit
49
49
// / barriers, but that may significantly limit optimization.
50
50
// /
51
- // / 2. A poison mode supports debugging by setting the poisonRefs flag on any
52
- // / destroys that have been hoisted. This forces the shadow variables to be
53
- // / overwritten with a sentinel, preventing the debugger from seeing
54
- // / deinitialized objects.
55
- // /
56
51
// ===----------------------------------------------------------------------===//
57
52
58
53
#define DEBUG_TYPE " copy-propagation"
@@ -441,18 +436,16 @@ static DestroyValueInst *findDestroyOnCFGEdge(SILBasicBlock *edgeBB,
441
436
// / canonicalizing other values. This is especially important when
442
437
// / canonicalization is called within an iterative worklist such as SILCombine.
443
438
void CanonicalizeOSSALifetime::findOrInsertDestroyOnCFGEdge (
444
- SILBasicBlock *predBB, SILBasicBlock *succBB, bool needsPoison ) {
439
+ SILBasicBlock *predBB, SILBasicBlock *succBB) {
445
440
446
441
assert (succBB->getSinglePredecessorBlock () == predBB
447
442
&& " value is live-out on another predBB successor: critical edge?" );
448
443
auto *di = findDestroyOnCFGEdge (succBB, currentDef);
449
444
if (!di) {
450
445
auto pos = succBB->begin ();
451
- // TODO: to improve debugability, consider advancing the poison position
452
- // ahead of operations that are known not to access weak references.
453
446
SILBuilderWithScope builder (pos);
454
447
auto loc = RegularLocation::getAutoGeneratedLocation (pos->getLoc ());
455
- di = builder.createDestroyValue (loc, currentDef, needsPoison );
448
+ di = builder.createDestroyValue (loc, currentDef);
456
449
getCallbacks ().createdNewInst (di);
457
450
}
458
451
consumes.recordFinalConsume (di);
@@ -466,7 +459,6 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyOnCFGEdge(
466
459
// / Create a final destroy, immediately after `pos`.
467
460
static void insertDestroyAtInst (SILBasicBlock::iterator pos,
468
461
DestroyValueInst *existingDestroy, SILValue def,
469
- bool needsPoison,
470
462
CanonicalOSSAConsumeInfo &consumes,
471
463
InstModCallbacks &callbacks) {
472
464
if (existingDestroy) {
@@ -476,15 +468,11 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos,
476
468
}
477
469
}
478
470
consumes.recordFinalConsume (existingDestroy);
479
- // Avoid resetting poison just in case this runs twice.
480
- if (needsPoison) {
481
- existingDestroy->setPoisonRefs (true );
482
- }
483
471
return ;
484
472
}
485
473
SILBuilderWithScope builder (pos);
486
474
auto loc = RegularLocation::getAutoGeneratedLocation ((*pos).getLoc ());
487
- auto *di = builder.createDestroyValue (loc, def, needsPoison );
475
+ auto *di = builder.createDestroyValue (loc, def);
488
476
callbacks.createdNewInst (di);
489
477
consumes.recordFinalConsume (di);
490
478
@@ -498,15 +486,12 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos,
498
486
//
499
487
// TODO: This has become quite a hack. Instead, the final liveness boundary
500
488
// should be returned in a data structure along with summary information about
501
- // each block. Then any special logic for handling existing destroys, debug
502
- // values, and poison should be applied to that block summary which can provide
503
- // the input to rewriteCopies.
489
+ // each block. Then any special logic for handling existing destroys and debug
490
+ // values should be applied to that block summary which can provide the input
491
+ // to rewriteCopies.
504
492
void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock (SILBasicBlock *bb) {
505
493
auto *defInst = currentDef->getDefiningInstruction ();
506
494
DestroyValueInst *existingDestroy = nullptr ;
507
- bool existingDestroyNeedsPoison = false ;
508
- // needsPoison is true as soon as an existingDestroy is seen, but sticky.
509
- bool needsPoison = false ;
510
495
auto instIter = bb->getTerminator ()->getIterator ();
511
496
while (true ) {
512
497
auto *inst = &*instIter;
@@ -524,36 +509,16 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
524
509
// Insert a destroy after this non-consuming use.
525
510
if (isa<TermInst>(inst)) {
526
511
for (auto &succ : bb->getSuccessors ()) {
527
- findOrInsertDestroyOnCFGEdge (bb, succ, poisonRefsMode );
512
+ findOrInsertDestroyOnCFGEdge (bb, succ);
528
513
}
529
514
} else {
530
- if (existingDestroy) {
531
- needsPoison = existingDestroyNeedsPoison;
532
- }
533
515
insertDestroyAtInst (std::next (instIter), existingDestroy, currentDef,
534
- needsPoison, consumes, getCallbacks ());
516
+ consumes, getCallbacks ());
535
517
}
536
518
return ;
537
519
case PrunedLiveness::LifetimeEndingUse:
538
- if (!needsPoison) {
539
- // This use becomes a final consume.
540
- consumes.recordFinalConsume (inst);
541
- return ;
542
- }
543
- // If a destroy needs poison, simply make it so.
544
- auto *destroy = dyn_cast<DestroyValueInst>(inst);
545
- // Reuse an existing one if we need to.
546
- if (!destroy) {
547
- destroy = existingDestroy;
548
- needsPoison = existingDestroyNeedsPoison;
549
- }
550
- if (destroy) {
551
- destroy->setPoisonRefs (needsPoison);
552
- consumes.recordFinalConsume (destroy);
553
- return ;
554
- }
555
- // Put this in the set of final consumes that need poison injection.
556
- consumes.recordNeedsPoison (inst);
520
+ // This use becomes a final consume.
521
+ consumes.recordFinalConsume (inst);
557
522
return ;
558
523
}
559
524
// This is not a potential last user. Keep scanning.
@@ -568,33 +533,21 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
568
533
destroy->getOperand ());
569
534
if (destroyDef == currentDef) {
570
535
existingDestroy = destroy;
571
- // If this destroy is reused, it is not poisoned.
572
- existingDestroyNeedsPoison = needsPoison;
573
- if (poisonRefsMode) {
574
- // Any earlier consume will be poisoned.
575
- needsPoison = true ;
576
- }
577
536
}
578
537
}
579
538
}
580
539
if (instIter == bb->begin ()) {
581
540
assert (cast<SILArgument>(currentDef)->getParent () == bb);
582
- if (existingDestroy) {
583
- needsPoison = existingDestroyNeedsPoison;
584
- }
585
- insertDestroyAtInst (instIter, existingDestroy, currentDef, needsPoison,
586
- consumes, getCallbacks ());
541
+ insertDestroyAtInst (instIter, existingDestroy, currentDef, consumes,
542
+ getCallbacks ());
587
543
return ;
588
544
}
589
545
--instIter;
590
546
// If the original def is reached, this is a dead live range. Insert a
591
547
// destroy immediately after the def.
592
548
if (&*instIter == defInst) {
593
- if (existingDestroy) {
594
- needsPoison = existingDestroyNeedsPoison;
595
- }
596
549
insertDestroyAtInst (std::next (instIter), existingDestroy, currentDef,
597
- needsPoison, consumes, getCallbacks ());
550
+ consumes, getCallbacks ());
598
551
return ;
599
552
}
600
553
}
@@ -634,11 +587,8 @@ void CanonicalizeOSSALifetime::findOrInsertDestroys() {
634
587
// Continue searching upward to find the pruned liveness boundary.
635
588
for (auto *predBB : bb->getPredecessorBlocks ()) {
636
589
if (liveness.getBlockLiveness (predBB) == PrunedLiveBlocks::LiveOut) {
637
- findOrInsertDestroyOnCFGEdge (predBB, bb, poisonRefsMode );
590
+ findOrInsertDestroyOnCFGEdge (predBB, bb);
638
591
} else {
639
- if (poisonRefsMode) {
640
- remnantLiveOutBlocks.insert (predBB);
641
- }
642
592
blockWorklist.insert (predBB);
643
593
}
644
594
}
@@ -679,13 +629,6 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
679
629
LLVM_DEBUG (llvm::dbgs () << " Removing " << *destroy);
680
630
++NumDestroysEliminated;
681
631
}
682
- // If another destroy is reachable on this path, then poison this
683
- // destroy. It will already be poisoned if it was inserted on a CFG edge
684
- // or another destroy occurs later in the same block.
685
- if (!destroy->poisonRefs ()
686
- && remnantLiveOutBlocks.count (destroy->getParent ())) {
687
- destroy->setPoisonRefs (true );
688
- }
689
632
return true ;
690
633
}
691
634
@@ -702,20 +645,6 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
702
645
return false ;
703
646
}
704
647
705
- // Final consumes that need poison, also need a copy.
706
- if (consumes.needsPoison (user)) {
707
- maybeNotifyMoveOnlyCopy (use);
708
- return false ;
709
- }
710
-
711
- // If another destroy is reachable on this path, then this consume needs
712
- // poison even though findOrInsertDestroyInBlock didn't know that.
713
- if (remnantLiveOutBlocks.count (user->getParent ())) {
714
- consumes.recordNeedsPoison (user);
715
- maybeNotifyMoveOnlyCopy (use);
716
- return false ;
717
- }
718
-
719
648
// Ok, this is a final user that isn't a destroy_value. Notify our caller if
720
649
// we were asked to.
721
650
//
@@ -762,10 +691,6 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
762
691
}
763
692
assert (!consumes.hasUnclaimedConsumes ());
764
693
765
- // Insert destroys wherever poison is needed. This may recover more debug
766
- // values, erasing them from the debugValues set.
767
- injectPoison ();
768
-
769
694
// Add any debug_values from Dead blocks into the debugAfterConsume set.
770
695
for (auto *dvi : debugValues) {
771
696
if (liveness.getBlockLiveness (dvi->getParent ()) == PrunedLiveBlocks::Dead) {
@@ -785,54 +710,10 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
785
710
}
786
711
}
787
712
788
- // / Insert poison destroys after each consume that needs poison.
789
- void CanonicalizeOSSALifetime::injectPoison () {
790
- for (SILInstruction *consume : consumes.getNeedsPoisonConsumes ()) {
791
- auto createDestroy = [&](SILBuilder &builder) {
792
- auto loc = RegularLocation::getAutoGeneratedLocation (
793
- builder.getInsertionPoint ()->getLoc ());
794
- auto *di = builder.createDestroyValue (loc, currentDef,
795
- /* needsPoison*/ true );
796
- getCallbacks ().createdNewInst (di);
797
- ++NumDestroysGenerated;
798
- LLVM_DEBUG (llvm::dbgs () << " Destroy at last use " << *di);
799
- };
800
- if (auto *branch = dyn_cast<BranchInst>(consume)) {
801
- // At a phi, destroy the value before branching.
802
- //
803
- // A copy must have been created for the branch operand during
804
- // rewriteCopies (but there's no easy way to assert that here).
805
- SILBuilderWithScope builder (branch);
806
- createDestroy (builder);
807
- } else {
808
- // For normal instructions and non-phi block terminators, destroy the
809
- // value after the operation.
810
- SILBuilderWithScope::insertAfter (consume, createDestroy);
811
- }
812
- }
813
- }
814
-
815
713
// ===----------------------------------------------------------------------===//
816
714
// MARK: Top-Level API
817
715
// ===----------------------------------------------------------------------===//
818
716
819
- // / True if \p def should be canonicalized in poisonRefs mode.
820
- // /
821
- // / Currently, we only handle owned values because, for now, the goal is to
822
- // / shorten lifetimes to mimic -O behavior, not to remove copies.
823
- // /
824
- // / IRGen must also have support for injecting poison into values of this type.
825
- static bool shouldCanonicalizeWithPoison (SILValue def) {
826
- assert (def->getType ().isObject () && " only SIL values are canonicalized" );
827
- auto objectTy = def->getType ().unwrapOptionalType ();
828
-
829
- // TODO: Handle structs, enums, and tuples.
830
- //
831
- // TODO: Functions are not currently handled because of closure lifetime
832
- // bugs. Noescape closures don't always have a dependence.
833
- return objectTy.isAnyClassReferenceType ();
834
- }
835
-
836
717
// / Canonicalize a single extended owned lifetime.
837
718
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime (SILValue def) {
838
719
if (def->getOwnershipKind () != OwnershipKind::Owned)
@@ -841,9 +722,6 @@ bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
841
722
if (def->isLexical ())
842
723
return false ;
843
724
844
- if (poisonRefsMode && !shouldCanonicalizeWithPoison (def))
845
- return false ;
846
-
847
725
LLVM_DEBUG (llvm::dbgs () << " Canonicalizing: " << def);
848
726
849
727
// Note: There is no need to register callbacks with this utility. 'onDelete'
0 commit comments