Skip to content

Commit f2760bb

Browse files
committed
Enable DCE on OSSA
1 parent 8d217f3 commit f2760bb

File tree

4 files changed

+753
-72
lines changed

4 files changed

+753
-72
lines changed

lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

Lines changed: 144 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
2121
#include "swift/SILOptimizer/PassManager/Passes.h"
2222
#include "swift/SILOptimizer/PassManager/Transforms.h"
23+
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
24+
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
2325
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2426
#include "llvm/ADT/DenseMap.h"
2527
#include "llvm/ADT/SmallPtrSet.h"
@@ -45,6 +47,17 @@ static bool seemsUseful(SILInstruction *I) {
4547
if (isa<BeginAccessInst>(I))
4648
return false;
4749

50+
// Even though begin_borrow/destroy_value/copy_value have side-effects, they
51+
// can be DCE'ed if they do not have useful dependencies/reverse dependencies
52+
if (isa<BeginBorrowInst>(I))
53+
return false;
54+
55+
if (isa<DestroyValueInst>(I))
56+
return false;
57+
58+
if (isa<CopyValueInst>(I))
59+
return false;
60+
4861
if (I->mayHaveSideEffects())
4962
return true;
5063

@@ -94,15 +107,16 @@ class DCE : public SILFunctionTransform {
94107
// Dependencies which go in the reverse direction. Usually for a pair
95108
// %1 = inst_a
96109
// inst_b(%1)
97-
// the dependency goes from inst_b to inst_a: if inst_b is alive then also
98-
// inst_a is alive.
110+
// the dependency goes from inst_b to inst_a: if inst_b is alive then
111+
// inst_a is also alive.
99112
// For some instructions the dependency is exactly the other way round, e.g.
100113
// %1 = inst_which_can_fail
101114
// cond_fail(%1)
102115
// In this case cond_fail is alive only if inst_which_can_fail is alive.
103116
// The key of this map is the source of the dependency (inst_a), the
104-
// value is the destination (inst_b).
105-
llvm::DenseMap<SILInstruction *, SILInstruction *> ReverseDependencies;
117+
// value is the set of instructions dependent on it (inst_b).
118+
llvm::DenseMap<SILValue, SmallPtrSet<SILInstruction *, 4>>
119+
ReverseDependencies;
106120

107121
/// Tracks if the pass changed branches.
108122
bool BranchesChanged;
@@ -115,11 +129,6 @@ class DCE : public SILFunctionTransform {
115129
CallsChanged = false;
116130

117131
SILFunction *F = getFunction();
118-
119-
// FIXME: Support ownership.
120-
if (F->hasOwnership())
121-
return;
122-
123132
auto *DA = PM->getAnalysis<PostDominanceAnalysis>();
124133
PDT = DA->get(F);
125134

@@ -150,8 +159,12 @@ class DCE : public SILFunctionTransform {
150159
using InvalidationKind = SILAnalysis::InvalidationKind;
151160

152161
unsigned Inv = InvalidationKind::Instructions;
153-
if (CallsChanged) Inv |= (unsigned) InvalidationKind::Calls;
154-
if (BranchesChanged) Inv |= (unsigned) InvalidationKind::Branches;
162+
if (CallsChanged)
163+
Inv |= (unsigned)InvalidationKind::Calls;
164+
if (BranchesChanged) {
165+
removeUnreachableBlocks(*F);
166+
Inv |= (unsigned)InvalidationKind::Branches;
167+
}
155168

156169
invalidateAnalysis(SILAnalysis::InvalidationKind(Inv));
157170
}
@@ -164,7 +177,9 @@ class DCE : public SILFunctionTransform {
164177

165178
bool precomputeControlInfo(SILFunction &F);
166179
void markLive(SILFunction &F);
167-
void addReverseDependency(SILInstruction *From, SILInstruction *To);
180+
/// Record a reverse dependency from \p from to \p to meaning \p to is live
181+
/// if \p from is also live.
182+
void addReverseDependency(SILValue from, SILInstruction *to);
168183
bool removeDead(SILFunction &F);
169184

170185
void computeLevelNumbers(PostDomTreeNode *root);
@@ -186,7 +201,9 @@ class DCE : public SILFunctionTransform {
186201
llvm::SmallPtrSetImpl<SILBasicBlock *> &);
187202
SILBasicBlock *nearestUsefulPostDominator(SILBasicBlock *Block);
188203
void replaceBranchWithJump(SILInstruction *Inst, SILBasicBlock *Block);
189-
204+
/// If \p value is live, insert a lifetime ending operation in ossa.
205+
/// destroy_value for @owned value and end_borrow for a @guaranteed value.
206+
void endLifetimeOfLiveValue(SILValue value, SILInstruction *insertPt);
190207
};
191208

192209
// Keep track of the fact that V is live and add it to our worklist
@@ -220,7 +237,7 @@ void DCE::markValueLive(SILNode *V) {
220237
/// Gets the producing instruction of a cond_fail condition. Currently these
221238
/// are overflow builtins but may be extended to other instructions in the
222239
/// future.
223-
static SILInstruction *getProducer(CondFailInst *CFI) {
240+
static BuiltinInst *getProducer(CondFailInst *CFI) {
224241
// Check for the pattern:
225242
// %1 = builtin "some_operation_with_overflow"
226243
// %2 = tuple_extract %1
@@ -236,42 +253,47 @@ static SILInstruction *getProducer(CondFailInst *CFI) {
236253

237254
// Determine which instructions from this function we need to keep.
238255
void DCE::markLive(SILFunction &F) {
239-
240256
// Find the initial set of instructions in this function that appear
241257
// to be live in the sense that they are not trivially something we
242258
// can delete by examining only that instruction.
243259
for (auto &BB : F) {
244260
for (auto &I : BB) {
245-
if (auto *CFI = dyn_cast<CondFailInst>(&I)) {
246-
// A cond_fail is only alive if its (identifiable) producer is alive.
247-
if (SILInstruction *Prod = getProducer(CFI)) {
248-
addReverseDependency(Prod, CFI);
261+
switch (I.getKind()) {
262+
case SILInstructionKind::CondFailInst: {
263+
if (auto *Prod = getProducer(cast<CondFailInst>(&I))) {
264+
addReverseDependency(Prod, &I);
249265
} else {
250-
markValueLive(CFI);
266+
markValueLive(&I);
251267
}
252-
continue;
268+
break;
253269
}
254-
if (auto *FLI = dyn_cast<FixLifetimeInst>(&I)) {
255-
// A fix_lifetime (with a non-address type) is only alive if it's
256-
// definition is alive.
257-
SILValue Op = FLI->getOperand();
258-
auto *OpInst = Op->getDefiningInstruction();
259-
if (OpInst && !Op->getType().isAddress()) {
260-
addReverseDependency(OpInst, FLI);
270+
case SILInstructionKind::FixLifetimeInst: {
271+
SILValue Op = I.getOperand(0);
272+
if (!Op->getType().isAddress()) {
273+
addReverseDependency(Op, &I);
261274
} else {
262-
markValueLive(FLI);
275+
markValueLive(&I);
263276
}
264-
continue;
277+
break;
265278
}
266-
if (auto *endAccess = dyn_cast<EndAccessInst>(&I)) {
267-
addReverseDependency(endAccess->getBeginAccess(), &I);
268-
continue;
279+
case SILInstructionKind::EndAccessInst: {
280+
// An end_access is live only if it's begin_access is also live.
281+
auto *beginAccess = cast<EndAccessInst>(&I)->getBeginAccess();
282+
addReverseDependency(beginAccess, &I);
283+
break;
284+
}
285+
case SILInstructionKind::DestroyValueInst:
286+
case SILInstructionKind::EndBorrowInst: {
287+
// The instruction is live only if it's operand value is also live
288+
addReverseDependency(I.getOperand(0), &I);
289+
break;
290+
}
291+
default:
292+
if (seemsUseful(&I))
293+
markValueLive(&I);
269294
}
270-
if (seemsUseful(&I))
271-
markValueLive(&I);
272295
}
273296
}
274-
275297
// Now propagate liveness backwards from each instruction in our
276298
// worklist, adding new instructions to the worklist as we discover
277299
// more that we need to keep.
@@ -281,17 +303,11 @@ void DCE::markLive(SILFunction &F) {
281303
}
282304
}
283305

284-
// Records a reverse dependency. See DCE::ReverseDependencies.
285-
void DCE::addReverseDependency(SILInstruction *From, SILInstruction *To) {
286-
assert(!ReverseDependencies.lookup(To) &&
287-
"Target of reverse dependency already in map");
288-
SILInstruction *&Target = ReverseDependencies[From];
289-
SILInstruction *ExistingTarget = Target;
290-
Target = To;
291-
if (ExistingTarget) {
292-
// Create a single linked chain if From has multiple targets.
293-
ReverseDependencies[To] = ExistingTarget;
294-
}
306+
// Records a reverse dependency if needed. See DCE::ReverseDependencies.
307+
void DCE::addReverseDependency(SILValue from, SILInstruction *to) {
308+
LLVM_DEBUG(llvm::dbgs() << "Adding reverse dependency from " << from << " to "
309+
<< to);
310+
ReverseDependencies[from].insert(to);
295311
}
296312

297313
// Mark as live the terminator argument at index ArgIndex in Pred that
@@ -366,8 +382,10 @@ void DCE::propagateLiveBlockArgument(SILArgument *Arg) {
366382
for (Operand *DU : getDebugUses(Arg))
367383
markValueLive(DU->getUser());
368384

369-
if (isa<SILFunctionArgument>(Arg))
370-
return;
385+
// Mark all reverse dependencies on the Arg live
386+
for (auto *depInst : ReverseDependencies.lookup(Arg)) {
387+
markValueLive(depInst);
388+
}
371389

372390
auto *Block = Arg->getParent();
373391
auto ArgIndex = Arg->getIndex();
@@ -390,10 +408,13 @@ void DCE::propagateLiveness(SILInstruction *I) {
390408
for (Operand *DU : getDebugUses(result))
391409
markValueLive(DU->getUser());
392410

393-
// Handle all other reverse-dependency instructions, like cond_fail and
394-
// fix_lifetime. Only if the definition is alive, the user itself is alive.
395-
if (SILInstruction *DepInst = ReverseDependencies.lookup(I)) {
396-
markValueLive(DepInst);
411+
// Handle all other reverse-dependency instructions, like cond_fail,
412+
// fix_lifetime, destroy_value, etc. Only if the definition is alive, the
413+
// user itself is alive.
414+
for (auto res : I->getResults()) {
415+
for (auto *depInst : ReverseDependencies.lookup(res)) {
416+
markValueLive(depInst);
417+
}
397418
}
398419
return;
399420
}
@@ -471,22 +492,75 @@ void DCE::replaceBranchWithJump(SILInstruction *Inst, SILBasicBlock *Block) {
471492
(void)Branch;
472493
}
473494

495+
void DCE::endLifetimeOfLiveValue(SILValue value, SILInstruction *insertPt) {
496+
if (!LiveValues.count(value->getRepresentativeSILNodeInObject())) {
497+
return;
498+
}
499+
SILBuilderWithScope builder(insertPt);
500+
if (value.getOwnershipKind() == OwnershipKind::Owned) {
501+
builder.emitDestroyOperation(RegularLocation::getAutoGeneratedLocation(),
502+
value);
503+
}
504+
if (value.getOwnershipKind() == OwnershipKind::Guaranteed) {
505+
builder.emitEndBorrowOperation(RegularLocation::getAutoGeneratedLocation(),
506+
value);
507+
}
508+
}
509+
474510
// Remove the instructions that are not potentially useful.
475511
bool DCE::removeDead(SILFunction &F) {
476512
bool Changed = false;
477513

478514
for (auto &BB : F) {
479-
for (auto I = BB.args_begin(), E = BB.args_end(); I != E;) {
480-
auto Inst = *I++;
481-
if (LiveValues.count(Inst))
515+
for (unsigned i = 0; i < BB.getArguments().size();) {
516+
auto *arg = BB.getArgument(i);
517+
if (LiveValues.count(arg)) {
518+
i++;
482519
continue;
520+
}
483521

484522
LLVM_DEBUG(llvm::dbgs() << "Removing dead argument:\n");
485-
LLVM_DEBUG(Inst->dump());
523+
LLVM_DEBUG(arg->dump());
524+
525+
arg->replaceAllUsesWithUndef();
486526

487-
Inst->replaceAllUsesWithUndef();
527+
if (!F.hasOwnership() || arg->getType().isTrivial(F)) {
528+
i++;
529+
Changed = true;
530+
BranchesChanged = true;
531+
continue;
532+
}
488533

534+
if (!arg->isPhiArgument()) {
535+
// We cannot delete a non phi arg. If it was @owned, insert a
536+
// destroy_value, because its consuming user has already been marked
537+
// dead and will be deleted.
538+
// We do not have to end lifetime of a @guaranteed non phi arg.
539+
if (arg->getOwnershipKind() == OwnershipKind::Owned) {
540+
auto insertPt = getInsertAfterPoint(arg).getValue();
541+
SILBuilderWithScope builder(insertPt);
542+
auto *destroy = builder.createDestroyValue(insertPt->getLoc(), arg);
543+
LiveValues.insert(destroy->getRepresentativeSILNodeInObject());
544+
}
545+
i++;
546+
Changed = true;
547+
BranchesChanged = true;
548+
continue;
549+
}
550+
// In OSSA, we have to delete a dead phi argument and insert destroy or
551+
// end_borrow at its predecessors if the incoming values are live.
552+
// This is not necessary in non-OSSA, and will infact be incorrect.
553+
// Because, passing a value as a phi argument does not imply end of
554+
// lifetime in non-OSSA.
555+
BB.eraseArgument(i);
556+
for (auto *pred : BB.getPredecessorBlocks()) {
557+
auto *predTerm = pred->getTerminator();
558+
auto predArg = predTerm->getAllOperands()[i].get();
559+
endLifetimeOfLiveValue(predArg, predTerm);
560+
deleteEdgeValue(pred->getTerminator(), &BB, i);
561+
}
489562
Changed = true;
563+
BranchesChanged = true;
490564
}
491565

492566
for (auto I = BB.begin(), E = BB.end(); I != E; ) {
@@ -501,7 +575,11 @@ bool DCE::removeDead(SILFunction &F) {
501575
SILBasicBlock *postDom = nearestUsefulPostDominator(Inst->getParent());
502576
if (!postDom)
503577
continue;
504-
578+
579+
LLVM_DEBUG(llvm::dbgs() << "Replacing branch: ");
580+
LLVM_DEBUG(Inst->dump());
581+
LLVM_DEBUG(llvm::dbgs() << "with jump to: BB" << postDom->getDebugID());
582+
505583
replaceBranchWithJump(Inst, postDom);
506584
Inst->eraseFromParent();
507585
BranchesChanged = true;
@@ -514,6 +592,13 @@ bool DCE::removeDead(SILFunction &F) {
514592
LLVM_DEBUG(llvm::dbgs() << "Removing dead instruction:\n");
515593
LLVM_DEBUG(Inst->dump());
516594

595+
if (F.hasOwnership()) {
596+
for (auto &Op : Inst->getAllOperands()) {
597+
if (Op.isLifetimeEnding()) {
598+
endLifetimeOfLiveValue(Op.get(), Inst);
599+
}
600+
}
601+
}
517602
Inst->replaceAllUsesOfAllResultsWithUndef();
518603

519604
if (isa<ApplyInst>(Inst))

0 commit comments

Comments
 (0)