Skip to content

Commit b59280c

Browse files
Merge pull request #78624 from nate-chandler/rdar142520491_2
[OSSACanonicalizeOwned] Fix liveness passed to completion.
2 parents 29bcd2c + ebf6028 commit b59280c

File tree

7 files changed

+194
-80
lines changed

7 files changed

+194
-80
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
@@ -412,6 +416,11 @@ class SILCombiner :
412416
/// Perform one SILCombine iteration.
413417
bool doOneIteration(SILFunction &F, unsigned Iteration);
414418

419+
void processInstruction(SILInstruction *instruction,
420+
SILCombineCanonicalize &scCanonicalize,
421+
bool &MadeChange);
422+
friend test::SILCombinerProcessInstruction;
423+
415424
/// Add reachable code to the worklist. Meant to be used when starting to
416425
/// process a new function.
417426
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,3 +857,30 @@ exit:
857857
%retval = tuple ()
858858
return %retval : $()
859859
}
860+
861+
// CHECK-LABEL: begin running test {{.*}} on consume_copy_before_use_in_dead_end
862+
// CHECK-LABEL: sil [ossa] @consume_copy_before_use_in_dead_end : {{.*}} {
863+
// CHECK-NOT: destroy_value [dead_end]
864+
// CHECK-LABEL: } // end sil function 'consume_copy_before_use_in_dead_end'
865+
// CHECK-LABEL: end running test {{.*}} on consume_copy_before_use_in_dead_end
866+
sil [ossa] @consume_copy_before_use_in_dead_end : $@convention(thin) (@owned C) -> () {
867+
entry(%c : @owned $C):
868+
cond_br undef, exit, die
869+
870+
exit:
871+
destroy_value %c
872+
%retval = tuple ()
873+
return %retval
874+
875+
die:
876+
%move = move_value [lexical] %c
877+
%copy = copy_value %move
878+
specify_test "canonicalize_ossa_lifetime true false true %move"
879+
apply undef(%move) : $@convention(thin) (@owned C) -> ()
880+
%addr = alloc_stack $C
881+
%token = store_borrow %copy to %addr
882+
apply undef() : $@convention(thin) () -> ()
883+
%reload = load_borrow %token
884+
apply undef(%reload) : $@convention(thin) (@guaranteed C) -> ()
885+
unreachable
886+
}

test/SILOptimizer/sil_combine_apply_unit.sil

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ entry(%input : $*Input):
4040
%nunca = alloc_stack $Nunca
4141
%callee = function_ref @rdar127452206_callee : $@convention(thin) @Sendable @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for <Input, Nunca, Output>
4242
%convert = convert_function %callee : $@convention(thin) @Sendable @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for <Input, Nunca, Output> to $@convention(thin) @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for <Input, Nunca, Output> // user: %216
43-
specify_test "sil_combine_instruction @instruction[4]"
43+
specify_test "sil_combine_visit_instruction @instruction[4]"
4444
apply [nothrow] %convert(%output, %nunca, %input) : $@convention(thin) @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for <Input, Nunca, Output>
4545
dealloc_stack %nunca : $*Nunca
4646
dealloc_stack %output : $*Output
@@ -62,7 +62,7 @@ entry(%c : @owned $C):
6262
%borrowMaybeC = function_ref @borrowMaybeC : $@convention(thin) (@guaranteed Optional<C>) -> ()
6363
%borrowC = convert_function %borrowMaybeC to $@convention(thin) (@guaranteed C) -> ()
6464
%void = apply %borrowC(%c) : $@convention(thin) (@guaranteed C) -> ()
65-
specify_test "sil_combine_instruction %void"
65+
specify_test "sil_combine_visit_instruction %void"
6666
destroy_value %c
6767
%retval = tuple ()
6868
return %retval
@@ -89,7 +89,7 @@ entry(%c : @owned $C, %c2 : @owned $C):
8989
%borrowMaybeC2 = function_ref @borrowMaybeC2 : $@convention(thin) (@guaranteed Optional<C>, @guaranteed Optional<C>) -> ()
9090
%borrowC2 = convert_function %borrowMaybeC2 to $@convention(thin) (@guaranteed C, @guaranteed C) -> ()
9191
%void = apply %borrowC2(%c, %c2) : $@convention(thin) (@guaranteed C, @guaranteed C) -> ()
92-
specify_test "sil_combine_instruction %void"
92+
specify_test "sil_combine_visit_instruction %void"
9393
destroy_value %c
9494
destroy_value %c2
9595
%retval = tuple ()
@@ -116,7 +116,7 @@ sil [ossa] @convert_function__to_optional__owned_as_guaranteed__3 : $@convention
116116
entry(%c : @owned $C):
117117
%borrowMaybeC = function_ref @borrowMaybeCThrowing : $@convention(thin) (@guaranteed Optional<C>) -> (@error Error)
118118
%borrowC = convert_function %borrowMaybeC to $@convention(thin) (@guaranteed C) -> (@error Error)
119-
specify_test "sil_combine_instruction @instruction"
119+
specify_test "sil_combine_visit_instruction @instruction"
120120
try_apply %borrowC(%c) : $@convention(thin) (@guaranteed C) -> (@error Error),
121121
normal success,
122122
error failure
@@ -160,7 +160,7 @@ sil [ossa] @convert_function__to_optional__owned_as_guaranteed__4 : $@convention
160160
entry(%c : @owned $C, %c2 : @owned $C):
161161
%borrowMaybeC2 = function_ref @borrowMaybeC2Throwing : $@convention(thin) (@guaranteed Optional<C>, @guaranteed Optional<C>) -> (@error Error)
162162
%borrowC2 = convert_function %borrowMaybeC2 to $@convention(thin) (@guaranteed C, @guaranteed C) -> (@error Error)
163-
specify_test "sil_combine_instruction @instruction"
163+
specify_test "sil_combine_visit_instruction @instruction"
164164
try_apply %borrowC2(%c, %c2) : $@convention(thin) (@guaranteed C, @guaranteed C) -> (@error Error),
165165
normal success,
166166
error failure

0 commit comments

Comments
 (0)