Skip to content

Commit 1b9d4c7

Browse files
Merge pull request #78674 from nate-chandler/cherrypick/release/6.1/rdar142520491_2
6.1: [OSSACanonicalizeOwned] Fix liveness passed to completion.
2 parents 714ecbd + 2848bc7 commit 1b9d4c7

File tree

7 files changed

+204
-89
lines changed

7 files changed

+204
-89
lines changed

lib/SIL/Utils/PrettyStackTrace.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ void PrettyStackTraceSILFunction::printFunctionInfo(llvm::raw_ostream &out) cons
9696
if (SILPrintOnError)
9797
func->print(out);
9898
if (SILPrintModuleOnError)
99-
func->getModule().print(out);
99+
func->getModule().print(out, func->getModule().getSwiftModule());
100100
}
101101

102102
void PrettyStackTraceSILNode::print(llvm::raw_ostream &out) const {

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ void PrunedLiveRange<LivenessWithDefs>::computeBoundary(
860860
// Visit each post-dominating block as the starting point for a
861861
// backward CFG traversal.
862862
for (auto *block : postDomBlocks) {
863-
blockWorklist.push(block);
863+
blockWorklist.pushIfNotVisited(block);
864864
}
865865
while (auto *block = blockWorklist.pop()) {
866866
// Process each block that has not been visited and is not LiveOut.

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 114 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ void SILCombiner::addReachableCodeToWorklist(SILBasicBlock *BB) {
120120
// Implementation
121121
//===----------------------------------------------------------------------===//
122122

123+
namespace swift {
124+
123125
// Define a CanonicalizeInstruction subclass for use in SILCombine.
124126
class SILCombineCanonicalize final : CanonicalizeInstruction {
125127
SmallSILInstructionWorklist<256> &Worklist;
@@ -162,6 +164,8 @@ class SILCombineCanonicalize final : CanonicalizeInstruction {
162164
}
163165
};
164166

167+
} // end namespace swift
168+
165169
SILCombiner::SILCombiner(SILFunctionTransform *trans,
166170
bool removeCondFails, bool enableCopyPropagation) :
167171
parentTransform(trans),
@@ -414,99 +418,136 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) {
414418
if (!parentTransform->continueWithNextSubpassRun(I))
415419
return false;
416420

417-
// Check to see if we can DCE the instruction.
418-
if (isInstructionTriviallyDead(I)) {
419-
LLVM_DEBUG(llvm::dbgs() << "SC: DCE: " << *I << '\n');
420-
eraseInstFromFunction(*I);
421-
++NumDeadInst;
422-
MadeChange = true;
423-
continue;
424-
}
421+
processInstruction(I, scCanonicalize, MadeChange);
422+
}
423+
424+
Worklist.resetChecked();
425+
return MadeChange;
426+
}
427+
428+
void SILCombiner::processInstruction(SILInstruction *I,
429+
SILCombineCanonicalize &scCanonicalize,
430+
bool &MadeChange) {
431+
// Check to see if we can DCE the instruction.
432+
if (isInstructionTriviallyDead(I)) {
433+
LLVM_DEBUG(llvm::dbgs() << "SC: DCE: " << *I << '\n');
434+
eraseInstFromFunction(*I);
435+
++NumDeadInst;
436+
MadeChange = true;
437+
return;
438+
}
425439
#ifndef NDEBUG
426-
std::string OrigIStr;
440+
std::string OrigIStr;
427441
#endif
428-
LLVM_DEBUG(llvm::raw_string_ostream SS(OrigIStr); I->print(SS);
429-
OrigIStr = SS.str(););
430-
LLVM_DEBUG(llvm::dbgs() << "SC: Visiting: " << OrigIStr << '\n');
442+
LLVM_DEBUG(llvm::raw_string_ostream SS(OrigIStr); I->print(SS);
443+
OrigIStr = SS.str(););
444+
LLVM_DEBUG(llvm::dbgs() << "SC: Visiting: " << OrigIStr << '\n');
431445

432-
// Canonicalize the instruction.
433-
if (scCanonicalize.tryCanonicalize(I)) {
434-
MadeChange = true;
435-
continue;
436-
}
446+
// Canonicalize the instruction.
447+
if (scCanonicalize.tryCanonicalize(I)) {
448+
MadeChange = true;
449+
return;
450+
}
437451

438-
// If we have reached this point, all attempts to do simple simplifications
439-
// have failed. First if we have an owned forwarding value, we try to
440-
// sink. Otherwise, we perform the actual SILCombine operation.
441-
if (EnableSinkingOwnedForwardingInstToUses) {
442-
// If we have an ownership forwarding single value inst that forwards
443-
// through its first argument and it is trivially duplicatable, see if it
444-
// only has consuming uses. If so, we can duplicate the instruction into
445-
// the consuming use blocks and destroy any destroy_value uses of it that
446-
// we see. This makes it easier for SILCombine to fold instructions with
447-
// owned parameters since chains of these values will be in the same
448-
// block.
449-
if (auto *svi = dyn_cast<SingleValueInstruction>(I)) {
450-
if (auto fwdOp = ForwardingOperation(svi)) {
451-
if (fwdOp.getSingleForwardingOperand() &&
452-
SILValue(svi)->getOwnershipKind() == OwnershipKind::Owned) {
453-
// Try to sink the value. If we sank the value and deleted it,
454-
// continue. If we didn't optimize or sank but we are still able to
455-
// optimize further, we fall through to SILCombine below.
456-
if (trySinkOwnedForwardingInst(svi)) {
457-
continue;
458-
}
452+
// If we have reached this point, all attempts to do simple simplifications
453+
// have failed. First if we have an owned forwarding value, we try to
454+
// sink. Otherwise, we perform the actual SILCombine operation.
455+
if (EnableSinkingOwnedForwardingInstToUses) {
456+
// If we have an ownership forwarding single value inst that forwards
457+
// through its first argument and it is trivially duplicatable, see if it
458+
// only has consuming uses. If so, we can duplicate the instruction into
459+
// the consuming use blocks and destroy any destroy_value uses of it that
460+
// we see. This makes it easier for SILCombine to fold instructions with
461+
// owned parameters since chains of these values will be in the same
462+
// block.
463+
if (auto *svi = dyn_cast<SingleValueInstruction>(I)) {
464+
if (auto fwdOp = ForwardingOperation(svi)) {
465+
if (fwdOp.getSingleForwardingOperand() &&
466+
SILValue(svi)->getOwnershipKind() == OwnershipKind::Owned) {
467+
// Try to sink the value. If we sank the value and deleted it,
468+
// return. If we didn't optimize or sank but we are still able to
469+
// optimize further, we fall through to SILCombine below.
470+
if (trySinkOwnedForwardingInst(svi)) {
471+
return;
459472
}
460473
}
461474
}
462475
}
476+
}
463477

464-
// Then begin... SILCombine.
465-
Builder.setInsertionPoint(I);
478+
// Then begin... SILCombine.
479+
Builder.setInsertionPoint(I);
466480

467-
SILInstruction *currentInst = I;
468-
if (SILInstruction *Result = visit(I)) {
469-
++NumCombined;
470-
// Should we replace the old instruction with a new one?
471-
Worklist.replaceInstructionWithInstruction(I, Result
481+
SILInstruction *currentInst = I;
482+
if (SILInstruction *Result = visit(I)) {
483+
++NumCombined;
484+
// Should we replace the old instruction with a new one?
485+
Worklist.replaceInstructionWithInstruction(I, Result
472486
#ifndef NDEBUG
473-
,
474-
OrigIStr
487+
,
488+
OrigIStr
475489
#endif
476-
);
477-
currentInst = Result;
478-
MadeChange = true;
479-
}
490+
);
491+
currentInst = Result;
492+
MadeChange = true;
493+
}
480494

481-
// Eliminate copies created that this SILCombine iteration may have
482-
// introduced during OSSA-RAUW.
483-
canonicalizeOSSALifetimes(currentInst->isDeleted() ? nullptr : currentInst);
484-
485-
// Builder's tracking list has been accumulating instructions created by the
486-
// during this SILCombine iteration. To finish this iteration, go through
487-
// the tracking list and add its contents to the worklist and then clear
488-
// said list in preparation for the next iteration.
489-
for (SILInstruction *I : *Builder.getTrackingList()) {
490-
if (!I->isDeleted()) {
491-
LLVM_DEBUG(llvm::dbgs()
492-
<< "SC: add " << *I << " from tracking list to worklist\n");
493-
Worklist.add(I);
494-
}
495+
// Eliminate copies created that this SILCombine iteration may have
496+
// introduced during OSSA-RAUW.
497+
canonicalizeOSSALifetimes(currentInst->isDeleted() ? nullptr : currentInst);
498+
499+
// Builder's tracking list has been accumulating instructions created by the
500+
// during this SILCombine iteration. To finish this iteration, go through
501+
// the tracking list and add its contents to the worklist and then clear
502+
// said list in preparation for the next iteration.
503+
for (SILInstruction *I : *Builder.getTrackingList()) {
504+
if (!I->isDeleted()) {
505+
LLVM_DEBUG(llvm::dbgs()
506+
<< "SC: add " << *I << " from tracking list to worklist\n");
507+
Worklist.add(I);
495508
}
496-
Builder.getTrackingList()->clear();
497509
}
498-
499-
Worklist.resetChecked();
500-
return MadeChange;
510+
Builder.getTrackingList()->clear();
501511
}
502512

513+
namespace swift::test {
514+
struct SILCombinerProcessInstruction {
515+
void operator()(SILCombiner &combiner, SILInstruction *inst,
516+
SILCombineCanonicalize &canonicalizer, bool &madeChange) {
517+
combiner.processInstruction(inst, canonicalizer, madeChange);
518+
}
519+
};
520+
// Arguments:
521+
// - instruction: the instruction to be processed
522+
// - bool: remove cond_fails
523+
// - bool: enable lifetime canonicalization
524+
// Dumps:
525+
// - the function after the processing is attempted
526+
static FunctionTest SILCombineProcessInstruction(
527+
"sil_combine_process_instruction",
528+
[](auto &function, auto &arguments, auto &test) {
529+
auto inst = arguments.takeInstruction();
530+
auto removeCondFails = arguments.takeBool();
531+
auto enableCopyPropagation = arguments.takeBool();
532+
SILCombiner combiner(test.getPass(), removeCondFails,
533+
enableCopyPropagation);
534+
SILCombineCanonicalize canonicalizer(combiner.Worklist,
535+
*test.getDeadEndBlocks());
536+
bool madeChange = false;
537+
SILCombinerProcessInstruction()(combiner, inst, canonicalizer,
538+
madeChange);
539+
function.dump();
540+
});
541+
} // end namespace swift::test
542+
503543
namespace swift::test {
504544
// Arguments:
505-
// - instruction: the instruction to be canonicalized
545+
// - instruction: the instruction to be visited
506546
// Dumps:
507-
// - the function after the canonicalization is attempted
508-
static FunctionTest SILCombineCanonicalizeInstruction(
509-
"sil_combine_instruction", [](auto &function, auto &arguments, auto &test) {
547+
// - the function after the visitation is attempted
548+
static FunctionTest SILCombineVisitInstruction(
549+
"sil_combine_visit_instruction",
550+
[](auto &function, auto &arguments, auto &test) {
510551
SILCombiner combiner(test.getPass(), false, false);
511552
auto inst = arguments.takeInstruction();
512553
combiner.Builder.setInsertionPoint(inst);

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
namespace swift {
4848

4949
class AliasAnalysis;
50+
class SILCombineCanonicalize;
51+
namespace test {
52+
struct SILCombinerProcessInstruction;
53+
}
5054

5155
/// This is a class which maintains the state of the combiner and simplifies
5256
/// many operations such as removing/adding instructions and syncing them with
@@ -417,6 +421,11 @@ class SILCombiner :
417421
/// Perform one SILCombine iteration.
418422
bool doOneIteration(SILFunction &F, unsigned Iteration);
419423

424+
void processInstruction(SILInstruction *instruction,
425+
SILCombineCanonicalize &scCanonicalize,
426+
bool &MadeChange);
427+
friend test::SILCombinerProcessInstruction;
428+
420429
/// Add reachable code to the worklist. Meant to be used when starting to
421430
/// process a new function.
422431
void addReachableCodeToWorklist(SILBasicBlock *BB);

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,43 @@ void CanonicalizeOSSALifetime::extendLivenessToDeadEnds() {
285285
completeLiveness.updateForUse(destroy, /*lifetimeEnding*/ true);
286286
}
287287

288+
// Demote consuming uses within complete liveness to non-consuming uses.
289+
//
290+
// OSSALifetimeCompletion considers the lifetime of a single value. Such
291+
// lifetimes never continue beyond consumes.
292+
std::optional<llvm::SmallPtrSet<SILInstruction *, 8>> lastUsers;
293+
auto isConsumeOnBoundary = [&](SILInstruction *instruction) -> bool {
294+
if (!lastUsers) {
295+
// Avoid computing lastUsers if possible.
296+
auto *function = getCurrentDef()->getFunction();
297+
auto *deadEnds = deadEndBlocksAnalysis->get(function);
298+
llvm::SmallVector<SILBasicBlock *, 8> completeConsumingBlocks(
299+
consumingBlocks.getArrayRef());
300+
for (auto &block : *function) {
301+
if (!deadEnds->isDeadEnd(&block))
302+
continue;
303+
completeConsumingBlocks.push_back(&block);
304+
}
305+
PrunedLivenessBoundary boundary;
306+
liveness->computeBoundary(boundary, completeConsumingBlocks);
307+
308+
lastUsers.emplace();
309+
for (auto *lastUser : boundary.lastUsers) {
310+
lastUsers->insert(lastUser);
311+
}
312+
}
313+
return lastUsers->contains(instruction);
314+
};
315+
for (auto pair : liveness->getAllUsers()) {
316+
if (!pair.second.isEnding())
317+
continue;
318+
auto *instruction = pair.first;
319+
if (isConsumeOnBoundary(instruction))
320+
continue;
321+
// Demote instruction's lifetime-ending-ness to non-lifetime-ending.
322+
completeLiveness.updateForUse(pair.first, /*lifetimeEnding=*/false);
323+
}
324+
288325
OSSALifetimeCompletion::visitAvailabilityBoundary(
289326
getCurrentDef(), completeLiveness, [&](auto *unreachable, auto end) {
290327
if (end == OSSALifetimeCompletion::LifetimeEnd::Boundary) {

test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ exit(%phi : @owned $C, %typhi : $S):
162162
sil @empty : $@convention(thin) () -> () {
163163
[global: ]
164164
bb0:
165-
%0 = tuple ()
166-
return %0 : $()
167-
}
165+
%0 = tuple ()
166+
return %0 : $()
167+
}
168168

169169
// Even though the apply of %empty is not a deinit barrier, verify that the
170170
// destroy is not hoisted, because MoS is move-only.
@@ -568,16 +568,16 @@ entry(%c1 : @owned $C):
568568
// CHECK: bb0([[C1:%[^,]+]] : @owned $C):
569569
// CHECK: [[TAKE_C:%[^,]+]] = function_ref @takeC
570570
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
571-
// CHECK: cond_br undef, [[LEFT]], [[RIGHT]]
572-
// CHECK: [[LEFT]]:
571+
// CHECK: cond_br undef, [[LEFT]], [[RIGHT]]
572+
// CHECK: [[LEFT]]:
573573
// CHECK: [[M:%[^,]+]] = move_value [[C1]]
574574
// CHECK: apply [[TAKE_C]]([[M]])
575-
// CHECK: br [[EXIT]]
576-
// CHECK: [[RIGHT]]:
575+
// CHECK: br [[EXIT]]
576+
// CHECK: [[RIGHT]]:
577577
// CHECK: apply [[BARRIER]]()
578578
// CHECK: destroy_value [[C1]]
579-
// CHECK: br [[EXIT]]
580-
// CHECK: [[EXIT]]:
579+
// CHECK: br [[EXIT]]
580+
// CHECK: [[EXIT]]:
581581
// CHECK: apply [[BARRIER]]()
582582
// CHECK-LABEL: } // end sil function 'lexical_end_at_end_2'
583583
// CHECK-LABEL: end running test {{.*}} on lexical_end_at_end_2: canonicalize_ossa_lifetime
@@ -857,3 +857,31 @@ exit:
857857
%retval = tuple ()
858858
return %retval : $()
859859
}
860+
861+
862+
// CHECK-LABEL: begin running test {{.*}} on consume_copy_before_use_in_dead_end
863+
// CHECK-LABEL: sil [ossa] @consume_copy_before_use_in_dead_end : {{.*}} {
864+
// CHECK-NOT: destroy_value [dead_end]
865+
// CHECK-LABEL: } // end sil function 'consume_copy_before_use_in_dead_end'
866+
// CHECK-LABEL: end running test {{.*}} on consume_copy_before_use_in_dead_end
867+
sil [ossa] @consume_copy_before_use_in_dead_end : $@convention(thin) (@owned C) -> () {
868+
entry(%c : @owned $C):
869+
cond_br undef, exit, die
870+
871+
exit:
872+
destroy_value %c : $C
873+
%retval = tuple ()
874+
return %retval : $()
875+
876+
die:
877+
%move = move_value [lexical] %c : $C
878+
%copy = copy_value %move : $C
879+
specify_test "canonicalize_ossa_lifetime true false true %move"
880+
apply undef(%move) : $@convention(thin) (@owned C) -> ()
881+
%addr = alloc_stack $C
882+
%token = store_borrow %copy to %addr : $*C
883+
apply undef() : $@convention(thin) () -> ()
884+
%reload = load_borrow %token : $*C
885+
apply undef(%reload) : $@convention(thin) (@guaranteed C) -> ()
886+
unreachable
887+
}

0 commit comments

Comments
 (0)