Skip to content

Commit 129dadb

Browse files
committed
[CopyPropagation] Removed poison mode.
The Onone strategy will be to shorten lifetimes as little as possible while eliminating as many copies as are eliminated at -O.
1 parent 9ca5492 commit 129dadb

File tree

7 files changed

+57
-245
lines changed

7 files changed

+57
-245
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,6 @@ class CanonicalizeOSSALifetime {
249249
/// liveness may be pruned during canonicalization.
250250
bool pruneDebugMode;
251251

252-
/// If true, then new destroy_value instructions will be poison.
253-
bool poisonRefsMode;
254-
255252
/// If true and we are processing a value of move_only type, emit a diagnostic
256253
/// when-ever we need to insert a copy_value.
257254
std::function<void(Operand *)> moveOnlyCopyValueNotification;
@@ -348,12 +345,11 @@ class CanonicalizeOSSALifetime {
348345
}
349346

350347
CanonicalizeOSSALifetime(
351-
bool pruneDebugMode, bool poisonRefsMode,
352-
NonLocalAccessBlockAnalysis *accessBlockAnalysis, DominanceInfo *domTree,
353-
InstructionDeleter &deleter,
348+
bool pruneDebugMode, NonLocalAccessBlockAnalysis *accessBlockAnalysis,
349+
DominanceInfo *domTree, InstructionDeleter &deleter,
354350
std::function<void(Operand *)> moveOnlyCopyValueNotification = nullptr,
355351
std::function<void(Operand *)> moveOnlyFinalConsumingUse = nullptr)
356-
: pruneDebugMode(pruneDebugMode), poisonRefsMode(poisonRefsMode),
352+
: pruneDebugMode(pruneDebugMode),
357353
moveOnlyCopyValueNotification(moveOnlyCopyValueNotification),
358354
moveOnlyFinalConsumingUse(moveOnlyFinalConsumingUse),
359355
accessBlockAnalysis(accessBlockAnalysis), domTree(domTree),
@@ -414,11 +410,9 @@ class CanonicalizeOSSALifetime {
414410
void findOrInsertDestroys();
415411

416412
void findOrInsertDestroyOnCFGEdge(SILBasicBlock *predBB,
417-
SILBasicBlock *succBB, bool needsPoison);
413+
SILBasicBlock *succBB);
418414

419415
void rewriteCopies();
420-
421-
void injectPoison();
422416
};
423417

424418
} // end namespace swift

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,8 +746,7 @@ struct MoveOnlyChecker {
746746
instToDelete->eraseFromParent();
747747
})),
748748
canonicalizer(
749-
false /*pruneDebugMode*/, false /*poisonRefsMode*/,
750-
accessBlockAnalysis, domTree, deleter,
749+
false /*pruneDebugMode*/, accessBlockAnalysis, domTree, deleter,
751750
[&](Operand *use) { consumingUsesNeedingCopy.push_back(use); },
752751
[&](Operand *use) { finalConsumingUses.push_back(use); }) {}
753752

lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,8 @@ bool MoveOnlyChecker::check(NonLocalAccessBlockAnalysis *accessBlockAnalysis,
401401
};
402402

403403
CanonicalizeOSSALifetime canonicalizer(
404-
false /*pruneDebugMode*/, false /*poisonRefsMode*/, accessBlockAnalysis,
405-
domTree, deleter, foundConsumingUseNeedingCopy,
406-
foundConsumingUseNotNeedingCopy);
404+
false /*pruneDebugMode*/, accessBlockAnalysis, domTree, deleter,
405+
foundConsumingUseNeedingCopy, foundConsumingUseNotNeedingCopy);
407406
auto moveIntroducers = llvm::makeArrayRef(moveIntroducersToProcess.begin(),
408407
moveIntroducersToProcess.end());
409408
SmallPtrSet<MarkMustCheckInst *, 4> valuesWithDiagnostics;

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ void SILCombiner::canonicalizeOSSALifetimes(SILInstruction *currentInst) {
350350
InstructionDeleter deleter(std::move(canonicalizeCallbacks));
351351

352352
DominanceInfo *domTree = DA->get(&Builder.getFunction());
353-
CanonicalizeOSSALifetime canonicalizer(
354-
false /*prune debug*/, false /*poison refs*/, NLABA, domTree, deleter);
353+
CanonicalizeOSSALifetime canonicalizer(false /*prune debug*/, NLABA, domTree,
354+
deleter);
355355
CanonicalizeBorrowScope borrowCanonicalizer(deleter);
356356

357357
while (!defsToCanonicalize.empty()) {

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,12 @@ class CopyPropagation : public SILFunctionTransform {
380380
/// If true, then borrow scopes will be canonicalized, allowing copies of
381381
/// guaranteed values to be optimized. Does *not* shrink the borrow scope.
382382
bool canonicalizeBorrows;
383-
/// If true, then new destroy_value instructions will be poison.
384-
bool poisonRefs;
385383

386384
public:
387385
CopyPropagation(bool pruneDebug, bool canonicalizeAll,
388-
bool canonicalizeBorrows, bool poisonRefs)
386+
bool canonicalizeBorrows)
389387
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
390-
canonicalizeBorrows(canonicalizeBorrows), poisonRefs(poisonRefs) {}
388+
canonicalizeBorrows(canonicalizeBorrows) {}
391389

392390
/// The entry point to this function transformation.
393391
void run() override;
@@ -439,8 +437,8 @@ void CopyPropagation::run() {
439437

440438
// canonicalizer performs all modifications through deleter's callbacks, so we
441439
// don't need to explicitly check for changes.
442-
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
443-
accessBlockAnalysis, domTree, deleter);
440+
CanonicalizeOSSALifetime canonicalizer(pruneDebug, accessBlockAnalysis,
441+
domTree, deleter);
444442

445443
// NOTE: We assume that the function is in reverse post order so visiting the
446444
// blocks and pushing begin_borrows as we see them and then popping them
@@ -583,12 +581,10 @@ void CopyPropagation::run() {
583581
// because it may negatively affect the debugging experience.
584582
SILTransform *swift::createMandatoryCopyPropagation() {
585583
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true,
586-
/*canonicalizeBorrows*/ false,
587-
/*poisonRefs*/ true);
584+
/*canonicalizeBorrows*/ false);
588585
}
589586

590587
SILTransform *swift::createCopyPropagation() {
591588
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true,
592-
/*canonicalizeBorrows*/ EnableRewriteBorrows,
593-
/*poisonRefs*/ false);
589+
/*canonicalizeBorrows*/ EnableRewriteBorrows);
594590
}

lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp

Lines changed: 14 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,6 @@
4848
/// alternative would be to model all end_access instructions as deinit
4949
/// barriers, but that may significantly limit optimization.
5050
///
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-
///
5651
//===----------------------------------------------------------------------===//
5752

5853
#define DEBUG_TYPE "copy-propagation"
@@ -441,18 +436,16 @@ static DestroyValueInst *findDestroyOnCFGEdge(SILBasicBlock *edgeBB,
441436
/// canonicalizing other values. This is especially important when
442437
/// canonicalization is called within an iterative worklist such as SILCombine.
443438
void CanonicalizeOSSALifetime::findOrInsertDestroyOnCFGEdge(
444-
SILBasicBlock *predBB, SILBasicBlock *succBB, bool needsPoison) {
439+
SILBasicBlock *predBB, SILBasicBlock *succBB) {
445440

446441
assert(succBB->getSinglePredecessorBlock() == predBB
447442
&& "value is live-out on another predBB successor: critical edge?");
448443
auto *di = findDestroyOnCFGEdge(succBB, currentDef);
449444
if (!di) {
450445
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.
453446
SILBuilderWithScope builder(pos);
454447
auto loc = RegularLocation::getAutoGeneratedLocation(pos->getLoc());
455-
di = builder.createDestroyValue(loc, currentDef, needsPoison);
448+
di = builder.createDestroyValue(loc, currentDef);
456449
getCallbacks().createdNewInst(di);
457450
}
458451
consumes.recordFinalConsume(di);
@@ -466,7 +459,6 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyOnCFGEdge(
466459
/// Create a final destroy, immediately after `pos`.
467460
static void insertDestroyAtInst(SILBasicBlock::iterator pos,
468461
DestroyValueInst *existingDestroy, SILValue def,
469-
bool needsPoison,
470462
CanonicalOSSAConsumeInfo &consumes,
471463
InstModCallbacks &callbacks) {
472464
if (existingDestroy) {
@@ -476,15 +468,11 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos,
476468
}
477469
}
478470
consumes.recordFinalConsume(existingDestroy);
479-
// Avoid resetting poison just in case this runs twice.
480-
if (needsPoison) {
481-
existingDestroy->setPoisonRefs(true);
482-
}
483471
return;
484472
}
485473
SILBuilderWithScope builder(pos);
486474
auto loc = RegularLocation::getAutoGeneratedLocation((*pos).getLoc());
487-
auto *di = builder.createDestroyValue(loc, def, needsPoison);
475+
auto *di = builder.createDestroyValue(loc, def);
488476
callbacks.createdNewInst(di);
489477
consumes.recordFinalConsume(di);
490478

@@ -498,15 +486,12 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos,
498486
//
499487
// TODO: This has become quite a hack. Instead, the final liveness boundary
500488
// 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.
504492
void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
505493
auto *defInst = currentDef->getDefiningInstruction();
506494
DestroyValueInst *existingDestroy = nullptr;
507-
bool existingDestroyNeedsPoison = false;
508-
// needsPoison is true as soon as an existingDestroy is seen, but sticky.
509-
bool needsPoison = false;
510495
auto instIter = bb->getTerminator()->getIterator();
511496
while (true) {
512497
auto *inst = &*instIter;
@@ -524,36 +509,16 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
524509
// Insert a destroy after this non-consuming use.
525510
if (isa<TermInst>(inst)) {
526511
for (auto &succ : bb->getSuccessors()) {
527-
findOrInsertDestroyOnCFGEdge(bb, succ, poisonRefsMode);
512+
findOrInsertDestroyOnCFGEdge(bb, succ);
528513
}
529514
} else {
530-
if (existingDestroy) {
531-
needsPoison = existingDestroyNeedsPoison;
532-
}
533515
insertDestroyAtInst(std::next(instIter), existingDestroy, currentDef,
534-
needsPoison, consumes, getCallbacks());
516+
consumes, getCallbacks());
535517
}
536518
return;
537519
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);
557522
return;
558523
}
559524
// This is not a potential last user. Keep scanning.
@@ -568,33 +533,21 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
568533
destroy->getOperand());
569534
if (destroyDef == currentDef) {
570535
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-
}
577536
}
578537
}
579538
}
580539
if (instIter == bb->begin()) {
581540
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());
587543
return;
588544
}
589545
--instIter;
590546
// If the original def is reached, this is a dead live range. Insert a
591547
// destroy immediately after the def.
592548
if (&*instIter == defInst) {
593-
if (existingDestroy) {
594-
needsPoison = existingDestroyNeedsPoison;
595-
}
596549
insertDestroyAtInst(std::next(instIter), existingDestroy, currentDef,
597-
needsPoison, consumes, getCallbacks());
550+
consumes, getCallbacks());
598551
return;
599552
}
600553
}
@@ -634,11 +587,8 @@ void CanonicalizeOSSALifetime::findOrInsertDestroys() {
634587
// Continue searching upward to find the pruned liveness boundary.
635588
for (auto *predBB : bb->getPredecessorBlocks()) {
636589
if (liveness.getBlockLiveness(predBB) == PrunedLiveBlocks::LiveOut) {
637-
findOrInsertDestroyOnCFGEdge(predBB, bb, poisonRefsMode);
590+
findOrInsertDestroyOnCFGEdge(predBB, bb);
638591
} else {
639-
if (poisonRefsMode) {
640-
remnantLiveOutBlocks.insert(predBB);
641-
}
642592
blockWorklist.insert(predBB);
643593
}
644594
}
@@ -679,13 +629,6 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
679629
LLVM_DEBUG(llvm::dbgs() << " Removing " << *destroy);
680630
++NumDestroysEliminated;
681631
}
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-
}
689632
return true;
690633
}
691634

@@ -702,20 +645,6 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
702645
return false;
703646
}
704647

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-
719648
// Ok, this is a final user that isn't a destroy_value. Notify our caller if
720649
// we were asked to.
721650
//
@@ -762,10 +691,6 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
762691
}
763692
assert(!consumes.hasUnclaimedConsumes());
764693

765-
// Insert destroys wherever poison is needed. This may recover more debug
766-
// values, erasing them from the debugValues set.
767-
injectPoison();
768-
769694
// Add any debug_values from Dead blocks into the debugAfterConsume set.
770695
for (auto *dvi : debugValues) {
771696
if (liveness.getBlockLiveness(dvi->getParent()) == PrunedLiveBlocks::Dead) {
@@ -785,54 +710,10 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
785710
}
786711
}
787712

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-
815713
//===----------------------------------------------------------------------===//
816714
// MARK: Top-Level API
817715
//===----------------------------------------------------------------------===//
818716

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-
836717
/// Canonicalize a single extended owned lifetime.
837718
bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
838719
if (def->getOwnershipKind() != OwnershipKind::Owned)
@@ -841,9 +722,6 @@ bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
841722
if (def->isLexical())
842723
return false;
843724

844-
if (poisonRefsMode && !shouldCanonicalizeWithPoison(def))
845-
return false;
846-
847725
LLVM_DEBUG(llvm::dbgs() << " Canonicalizing: " << def);
848726

849727
// Note: There is no need to register callbacks with this utility. 'onDelete'

0 commit comments

Comments
 (0)