Skip to content

Commit 7c6816f

Browse files
authored
Merge pull request swiftlang#26252 from gottesmm/pr-26f2be9085e39bcfada4ac0e174959d83d1bf8bd
[closure-lifetime-fixup] Delete dead trivial phi nodes.
2 parents 2450a79 + 1b127d9 commit 7c6816f

File tree

4 files changed

+198
-87
lines changed

4 files changed

+198
-87
lines changed

include/swift/SILOptimizer/Utils/CFG.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ TermInst *addNewEdgeValueToBranch(TermInst *Branch, SILBasicBlock *Dest,
4545
TermInst *changeEdgeValue(TermInst *Branch, SILBasicBlock *Dest, size_t Idx,
4646
SILValue Val);
4747

48+
/// Deletes the edge value between a branch and a destination basic block at the
49+
/// specified index. Asserts internally that the argument along the edge does
50+
/// not have uses.
51+
TermInst *deleteEdgeValue(TermInst *branch, SILBasicBlock *destBlock,
52+
size_t argIndex);
53+
54+
/// Erase the \p argIndex phi argument from \p block. Asserts that the argument
55+
/// is a /real/ phi argument. Removes all incoming values for the argument from
56+
/// predecessor terminators. Asserts internally that it only ever is given
57+
/// "true" phi argument.
58+
void erasePhiArgument(SILBasicBlock *block, unsigned argIndex);
59+
4860
/// Replace a branch target.
4961
///
5062
/// \param T The terminating instruction to modify.

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,89 @@ static void findReachableExitBlocks(SILInstruction *i,
118118
}
119119
}
120120

121+
/// We use this to ensure that we properly handle recursive cases by revisiting
122+
/// phi nodes that we are tracking. This just makes it easier to reproduce in a
123+
/// test case.
124+
static llvm::cl::opt<bool> ReverseInitialWorklist(
125+
"sil-closure-lifetime-fixup-reverse-phi-order", llvm::cl::init(false),
126+
llvm::cl::desc(
127+
"Reverse the order in which we visit phis for testing purposes"),
128+
llvm::cl::Hidden);
129+
130+
// Finally, we need to prune phis inserted by the SSA updater that
131+
// only take the .none from the entry block. This means that they are
132+
// not actually reachable from the .some() so we know that we do not
133+
// need to lifetime extend there at all. As an additional benefit, we
134+
// eliminate the need to balance these arguments to satisfy the
135+
// ownership verifier. This occurs since arguments are a place in SIL
136+
// where the trivialness of an enums case is erased.
137+
static void
138+
cleanupDeadTrivialPhiArgs(SILValue initialValue,
139+
SmallVectorImpl<SILPhiArgument *> &insertedPhis) {
140+
// Just for testing purposes.
141+
if (ReverseInitialWorklist) {
142+
std::reverse(insertedPhis.begin(), insertedPhis.end());
143+
}
144+
SmallVector<SILPhiArgument *, 8> worklist(insertedPhis.begin(),
145+
insertedPhis.end());
146+
sortUnique(insertedPhis);
147+
SmallVector<SILValue, 8> incomingValues;
148+
149+
while (!worklist.empty()) {
150+
// Clear the incoming values array after each iteration.
151+
SWIFT_DEFER { incomingValues.clear(); };
152+
153+
auto *phi = worklist.pop_back_val();
154+
{
155+
auto it = lower_bound(insertedPhis, phi);
156+
if (it == insertedPhis.end() || *it != phi)
157+
continue;
158+
}
159+
160+
// TODO: When we split true phi arguments from transformational terminators,
161+
// this will always succeed and the assert can go away.
162+
bool foundPhiValues = phi->getIncomingPhiValues(incomingValues);
163+
(void)foundPhiValues;
164+
assert(foundPhiValues && "Should always have 'true' phi arguments since "
165+
"these were inserted by the SSA updater.");
166+
if (llvm::any_of(incomingValues,
167+
[&](SILValue v) { return v != initialValue; }))
168+
continue;
169+
170+
// Remove it from our insertedPhis list to prevent us from re-visiting this.
171+
{
172+
auto it = lower_bound(insertedPhis, phi);
173+
assert((it != insertedPhis.end() && *it == phi) &&
174+
"Should have found the phi");
175+
insertedPhis.erase(it);
176+
}
177+
178+
// See if any of our users are branch or cond_br. If so, we may have
179+
// exposed additional unneeded phis. Add it back to the worklist in such a
180+
// case.
181+
for (auto *op : phi->getUses()) {
182+
auto *user = op->getUser();
183+
184+
if (!isa<BranchInst>(user) && !isa<CondBranchInst>(user))
185+
continue;
186+
187+
auto *termInst = cast<TermInst>(user);
188+
for (auto succBlockArgList : termInst->getSuccessorBlockArguments()) {
189+
copy_if(succBlockArgList, std::back_inserter(worklist),
190+
[&](SILPhiArgument *succArg) -> bool {
191+
auto it = lower_bound(insertedPhis, succArg);
192+
return it != insertedPhis.end() && *it == succArg;
193+
});
194+
}
195+
}
196+
197+
// Then RAUW the phi with the entryBlockOptionalNone and erase the
198+
// argument.
199+
phi->replaceAllUsesWith(initialValue);
200+
erasePhiArgument(phi->getParent(), phi->getIndex());
201+
}
202+
}
203+
121204
/// Extend the lifetime of the convert_escape_to_noescape's operand to the end
122205
/// of the function.
123206
///
@@ -157,7 +240,8 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
157240
// Ok. At this point we know that Cvt is not in the entry block... so we can
158241
// use SILSSAUpdater::GetValueInMiddleOfBlock() to extend the object's
159242
// lifetime respecting loops.
160-
SILSSAUpdater updater;
243+
SmallVector<SILPhiArgument *, 8> insertedPhis;
244+
SILSSAUpdater updater(&insertedPhis);
161245
updater.Initialize(optionalEscapingClosureTy);
162246

163247
// Create an Optional<() -> ()>.none in the entry block of the function and
@@ -166,10 +250,11 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
166250
// Since we know that Cvt is not in the entry block and this must be, we know
167251
// that it is safe to use the SSAUpdater's getValueInMiddleOfBlock with this
168252
// value.
169-
updater.AddAvailableValue(fn.getEntryBlock(), [&]() -> SILValue {
253+
SILValue entryBlockOptionalNone = [&]() -> SILValue {
170254
SILBuilderWithScope b(fn.getEntryBlock()->begin());
171255
return b.createOptionalNone(loc, optionalEscapingClosureTy);
172-
}());
256+
}();
257+
updater.AddAvailableValue(fn.getEntryBlock(), entryBlockOptionalNone);
173258

174259
// Create a copy of the convert_escape_to_no_escape and add it as an available
175260
// value to the SSA updater.
@@ -206,6 +291,14 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
206291
SILValue v = updater.GetValueAtEndOfBlock(block);
207292
SILBuilderWithScope(safeDestructionPt).createDestroyValue(loc, v);
208293
}
294+
295+
// Finally, we need to prune phis inserted by the SSA updater that only take
296+
// the .none from the entry block.
297+
//
298+
// TODO: Should we sort inserted phis before or after we initialize
299+
// the worklist or maybe backwards? We should investigate how the
300+
// SSA updater adds phi nodes to this list to resolve this question.
301+
cleanupDeadTrivialPhiArgs(entryBlockOptionalNone, insertedPhis);
209302
}
210303

211304
static SILInstruction *lookThroughRebastractionUsers(
@@ -724,16 +817,19 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
724817
auto optionalEscapingClosureTy =
725818
SILType::getOptionalType(sentinelClosure->getType());
726819

727-
SILSSAUpdater updater;
820+
SmallVector<SILPhiArgument *, 8> insertedPhis;
821+
SILSSAUpdater updater(&insertedPhis);
728822
updater.Initialize(optionalEscapingClosureTy);
729823

730824
// Create the Optional.none as the beginning available value.
825+
SILValue entryBlockOptionalNone;
731826
{
732827
SILBuilderWithScope b(fn.getEntryBlock()->begin());
733-
updater.AddAvailableValue(
734-
fn.getEntryBlock(),
735-
b.createOptionalNone(autoGenLoc, optionalEscapingClosureTy));
828+
entryBlockOptionalNone =
829+
b.createOptionalNone(autoGenLoc, optionalEscapingClosureTy);
830+
updater.AddAvailableValue(fn.getEntryBlock(), entryBlockOptionalNone);
736831
}
832+
assert(entryBlockOptionalNone);
737833

738834
// Then create the Optional.some(closure sentinel).
739835
//
@@ -790,6 +886,14 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
790886
}
791887
}
792888

889+
// Finally, we need to prune phis inserted by the SSA updater that only take
890+
// the .none from the entry block.
891+
//
892+
// TODO: Should we sort inserted phis before or after we initialize
893+
// the worklist or maybe backwards? We should investigate how the
894+
// SSA updater adds phi nodes to this list to resolve this question.
895+
cleanupDeadTrivialPhiArgs(entryBlockOptionalNone, insertedPhis);
896+
793897
return true;
794898
}
795899

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 4 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2712,84 +2712,6 @@ bool SimplifyCFG::tailDuplicateObjCMethodCallSuccessorBlocks() {
27122712
return Changed;
27132713
}
27142714

2715-
static void
2716-
deleteTriviallyDeadOperandsOfDeadArgument(MutableArrayRef<Operand> TermOperands,
2717-
unsigned DeadArgIndex) {
2718-
Operand &Op = TermOperands[DeadArgIndex];
2719-
auto *I = Op.get()->getDefiningInstruction();
2720-
if (!I)
2721-
return;
2722-
Op.set(SILUndef::get(Op.get()->getType(), *I->getFunction()));
2723-
recursivelyDeleteTriviallyDeadInstructions(I);
2724-
}
2725-
2726-
static void removeArgumentFromTerminator(SILBasicBlock *BB, SILBasicBlock *Dest,
2727-
int idx) {
2728-
TermInst *Branch = BB->getTerminator();
2729-
SILBuilderWithScope Builder(Branch);
2730-
LLVM_DEBUG(llvm::dbgs() << "remove dead argument " << idx << " from "
2731-
<< *Branch);
2732-
2733-
if (auto *CBI = dyn_cast<CondBranchInst>(Branch)) {
2734-
SmallVector<SILValue, 8> TrueArgs;
2735-
SmallVector<SILValue, 8> FalseArgs;
2736-
2737-
for (auto A : CBI->getTrueArgs())
2738-
TrueArgs.push_back(A);
2739-
2740-
for (auto A : CBI->getFalseArgs())
2741-
FalseArgs.push_back(A);
2742-
2743-
if (Dest == CBI->getTrueBB()) {
2744-
deleteTriviallyDeadOperandsOfDeadArgument(CBI->getTrueOperands(), idx);
2745-
TrueArgs.erase(TrueArgs.begin() + idx);
2746-
}
2747-
2748-
if (Dest == CBI->getFalseBB()) {
2749-
deleteTriviallyDeadOperandsOfDeadArgument(CBI->getFalseOperands(), idx);
2750-
FalseArgs.erase(FalseArgs.begin() + idx);
2751-
}
2752-
2753-
Builder.createCondBranch(CBI->getLoc(), CBI->getCondition(),
2754-
CBI->getTrueBB(), TrueArgs, CBI->getFalseBB(),
2755-
FalseArgs, CBI->getTrueBBCount(),
2756-
CBI->getFalseBBCount());
2757-
Branch->eraseFromParent();
2758-
return;
2759-
}
2760-
2761-
if (auto *BI = dyn_cast<BranchInst>(Branch)) {
2762-
SmallVector<SILValue, 8> Args;
2763-
2764-
for (auto A : BI->getArgs())
2765-
Args.push_back(A);
2766-
2767-
deleteTriviallyDeadOperandsOfDeadArgument(BI->getAllOperands(), idx);
2768-
Args.erase(Args.begin() + idx);
2769-
Builder.createBranch(BI->getLoc(), BI->getDestBB(), Args);
2770-
Branch->eraseFromParent();
2771-
return;
2772-
}
2773-
llvm_unreachable("unsupported terminator");
2774-
}
2775-
2776-
static void removeArgument(SILBasicBlock *BB, unsigned i) {
2777-
NumDeadArguments++;
2778-
BB->eraseArgument(i);
2779-
2780-
// Determine the set of predecessors in case any predecessor has
2781-
// two edges to this block (e.g. a conditional branch where both
2782-
// sides reach this block).
2783-
llvm::SetVector<SILBasicBlock *,SmallVector<SILBasicBlock *, 8>,
2784-
SmallPtrSet<SILBasicBlock *, 8>> PredBBs;
2785-
2786-
for (auto *Pred : BB->getPredecessorBlocks())
2787-
PredBBs.insert(Pred);
2788-
2789-
for (auto *Pred : PredBBs)
2790-
removeArgumentFromTerminator(Pred, BB, i);
2791-
}
2792-
27932715
namespace {
27942716

27952717
class ArgumentSplitter {
@@ -3026,7 +2948,8 @@ bool ArgumentSplitter::split() {
30262948
Worklist.push_back(A);
30272949
continue;
30282950
}
3029-
removeArgument(ParentBB, i);
2951+
erasePhiArgument(ParentBB, i);
2952+
++NumDeadArguments;
30302953
}
30312954

30322955
return true;
@@ -3766,7 +3689,8 @@ bool SimplifyCFG::simplifyArgs(SILBasicBlock *BB) {
37663689
continue;
37673690
}
37683691

3769-
removeArgument(BB, i);
3692+
erasePhiArgument(BB, i);
3693+
++NumDeadArguments;
37703694
Changed = true;
37713695
}
37723696

lib/SILOptimizer/Utils/CFG.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,77 @@ TermInst *swift::addNewEdgeValueToBranch(TermInst *Branch, SILBasicBlock *Dest,
7777
return NewBr;
7878
}
7979

80+
static void
81+
deleteTriviallyDeadOperandsOfDeadArgument(MutableArrayRef<Operand> termOperands,
82+
unsigned deadArgIndex) {
83+
Operand &op = termOperands[deadArgIndex];
84+
auto *i = op.get()->getDefiningInstruction();
85+
if (!i)
86+
return;
87+
op.set(SILUndef::get(op.get()->getType(), *i->getFunction()));
88+
recursivelyDeleteTriviallyDeadInstructions(i);
89+
}
90+
91+
// Our implementation assumes that our caller is attempting to remove a dead
92+
// SILPhiArgument from a SILBasicBlock and has already RAUWed the argument.
93+
TermInst *swift::deleteEdgeValue(TermInst *branch, SILBasicBlock *destBlock,
94+
size_t argIndex) {
95+
if (auto *cbi = dyn_cast<CondBranchInst>(branch)) {
96+
SmallVector<SILValue, 8> trueArgs;
97+
SmallVector<SILValue, 8> falseArgs;
98+
99+
copy(cbi->getTrueArgs(), std::back_inserter(trueArgs));
100+
copy(cbi->getFalseArgs(), std::back_inserter(falseArgs));
101+
102+
if (destBlock == cbi->getTrueBB()) {
103+
deleteTriviallyDeadOperandsOfDeadArgument(cbi->getTrueOperands(), argIndex);
104+
trueArgs.erase(trueArgs.begin() + argIndex);
105+
}
106+
107+
if (destBlock == cbi->getFalseBB()) {
108+
deleteTriviallyDeadOperandsOfDeadArgument(cbi->getFalseOperands(), argIndex);
109+
falseArgs.erase(falseArgs.begin() + argIndex);
110+
}
111+
112+
SILBuilderWithScope builder(cbi);
113+
auto *result = builder.createCondBranch(cbi->getLoc(), cbi->getCondition(),
114+
cbi->getTrueBB(), trueArgs, cbi->getFalseBB(),
115+
falseArgs, cbi->getTrueBBCount(),
116+
cbi->getFalseBBCount());
117+
branch->eraseFromParent();
118+
return result;
119+
}
120+
121+
if (auto *bi = dyn_cast<BranchInst>(branch)) {
122+
SmallVector<SILValue, 8> args;
123+
copy(bi->getArgs(), std::back_inserter(args));
124+
125+
deleteTriviallyDeadOperandsOfDeadArgument(bi->getAllOperands(), argIndex);
126+
args.erase(args.begin() + argIndex);
127+
auto *result = SILBuilderWithScope(bi).createBranch(bi->getLoc(), bi->getDestBB(), args);
128+
branch->eraseFromParent();
129+
return result;
130+
}
131+
132+
llvm_unreachable("unsupported terminator");
133+
}
134+
135+
void swift::erasePhiArgument(SILBasicBlock *block, unsigned argIndex) {
136+
assert(block->getArgument(argIndex)->isPhiArgument() &&
137+
"Only should be used on phi arguments");
138+
block->eraseArgument(argIndex);
139+
140+
// Determine the set of predecessors in case any predecessor has
141+
// two edges to this block (e.g. a conditional branch where both
142+
// sides reach this block).
143+
SmallVector<SILBasicBlock *, 8> predBlocks(block->pred_begin(),
144+
block->pred_end());
145+
sortUnique(predBlocks);
146+
147+
for (auto *pred : predBlocks)
148+
deleteEdgeValue(pred->getTerminator(), block, argIndex);
149+
}
150+
80151
/// Changes the edge value between a branch and destination basic block
81152
/// at the specified index. Changes all edges from \p Branch to \p Dest to carry
82153
/// the value.

0 commit comments

Comments
 (0)