Skip to content

Commit 2ccc089

Browse files
committed
SILGenFunction::mergeCleanupBlocks.
Avoid emitting unnecessary basic block for cleanup chains. This is a general approach that handles all cases while simplifying SILGen emission and keeping the CFG in a valid state during SILGen.
1 parent c8e4d2b commit 2ccc089

File tree

6 files changed

+103
-12
lines changed

6 files changed

+103
-12
lines changed

include/swift/SIL/BasicBlockUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ namespace swift {
2020
class SILFunction;
2121
class SILBasicBlock;
2222

23+
/// \brief Merge a basic block ending in a branch with its successor
24+
/// if possible.
25+
void mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB,
26+
SILBasicBlock *succBB);
27+
2328
/// A utility for finding dead-end blocks.
2429
///
2530
/// Dead-end blocks are blocks from which there is no path to the function exit

lib/SIL/BasicBlockUtils.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,31 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SIL/BasicBlockUtils.h"
14-
#include "swift/SIL/SILFunction.h"
14+
#include "swift/SIL/SILArgument.h"
1515
#include "swift/SIL/SILBasicBlock.h"
16+
#include "swift/SIL/SILFunction.h"
1617

1718
using namespace swift;
1819

20+
/// Merge the basic block with its successor if possible.
21+
void swift::mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB,
22+
SILBasicBlock *succBB) {
23+
auto *BI = cast<BranchInst>(BB->getTerminator());
24+
assert(succBB->getSinglePredecessorBlock());
25+
26+
// If there are any BB arguments in the destination, replace them with the
27+
// branch operands, since they must dominate the dest block.
28+
for (unsigned i = 0, e = BI->getArgs().size(); i != e; ++i)
29+
succBB->getArgument(i)->replaceAllUsesWith(BI->getArg(i));
30+
31+
BI->eraseFromParent();
32+
33+
// Move the instruction from the successor block to the current block.
34+
BB->spliceAtEnd(succBB);
35+
36+
succBB->eraseFromParent();
37+
}
38+
1939
//===----------------------------------------------------------------------===//
2040
// DeadEndBlocks
2141
//===----------------------------------------------------------------------===//

lib/SILGen/SILGenFunction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
401401
emitStmt(fd->getBody());
402402

403403
emitEpilog(fd);
404+
405+
mergeCleanupBlocks();
404406
}
405407

406408
void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
738738
/// The block should be empty and have no predecessors.
739739
void eraseBasicBlock(SILBasicBlock *block);
740740

741+
void mergeCleanupBlocks();
742+
741743
//===--------------------------------------------------------------------===//
742744
// Memory management
743745
//===--------------------------------------------------------------------===//

lib/SILGen/SILGenStmt.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "SwitchEnumBuilder.h"
2121
#include "swift/AST/DiagnosticsSIL.h"
2222
#include "swift/Basic/ProfileCounter.h"
23+
#include "swift/SIL/BasicBlockUtils.h"
2324
#include "swift/SIL/SILArgument.h"
2425
#include "llvm/Support/SaveAndRestore.h"
2526

@@ -97,6 +98,76 @@ void SILGenFunction::eraseBasicBlock(SILBasicBlock *block) {
9798
block->eraseFromParent();
9899
}
99100

101+
// Merge blocks during a single traversal of the block list. Only unconditional
102+
// branch edges are visited. Consequently, this takes only as much time as a
103+
// linked list traversal and requires no additional storage.
104+
//
105+
// For each block, check if it can be merged with its successor. Place the
106+
// merged block at the successor position in the block list.
107+
//
108+
// Typically, the successor occurs later in the list. This is most efficient
109+
// because merging moves instructions from the successor to the
110+
// predecessor. This way, instructions will only be moved once. Furthermore, the
111+
// merged block will be visited again to determine if it can be merged with it's
112+
// successor, and so on, so no edges are skipped.
113+
//
114+
// In rare cases, the predessor is merged with its earlier successor, which has
115+
// already been visited. If the successor can also be merged, then it has
116+
// already happened, and there is no need to revisit the merged block.
117+
void SILGenFunction::mergeCleanupBlocks() {
118+
for (auto bbPos = F.begin(), bbEnd = F.end(), nextPos = bbPos; bbPos != bbEnd;
119+
bbPos = nextPos) {
120+
// A forward iterator refering to the next unprocessed block in the block
121+
// list. If blocks are merged and moved, then this will be updated.
122+
nextPos = std::next(bbPos);
123+
124+
// Consider the current block as the predecessor.
125+
auto *predBB = &*bbPos;
126+
auto *BI = dyn_cast<BranchInst>(predBB->getTerminator());
127+
if (!BI)
128+
continue;
129+
130+
// predBB has an unconditional branch to succBB. If succBB has no other
131+
// predecessors, then merge the blocks.
132+
auto *succBB = BI->getDestBB();
133+
if (!succBB->getSinglePredecessorBlock())
134+
continue;
135+
136+
// Before merging, establish iterators that won't be invalidated by erasing
137+
// succBB. Use a reverse iterator to remember the position before a block.
138+
//
139+
// Remember the block before the current successor as a position for placing
140+
// the merged block.
141+
auto beforeSucc = std::next(SILFunction::reverse_iterator(succBB));
142+
143+
// Remember the position before the current predecessor to avoid skipping
144+
// blocks or revisiting blocks unnecessarilly.
145+
auto beforePred = std::next(SILFunction::reverse_iterator(predBB));
146+
// Since succBB will be erased, move before it.
147+
if (beforePred == SILFunction::reverse_iterator(succBB))
148+
++beforePred;
149+
150+
// Merge `predBB` with `succBB`. This erases `succBB`.
151+
mergeBasicBlockWithSingleSuccessor(predBB, succBB);
152+
153+
// If predBB is first in the list, then it must be the entry block which
154+
// cannot be moved.
155+
if (beforePred != F.rend()) {
156+
// Move the merged block into the successor position. (If the blocks are
157+
// not already adjacent, then the first is typically the trampoline.)
158+
assert(beforeSucc != F.rend()
159+
&& "entry block cannot have a predecessor.");
160+
predBB->moveAfter(&*beforeSucc);
161+
}
162+
// If after moving predBB there are no more blocks to process, then break.
163+
if (beforePred == F.rbegin())
164+
break;
165+
166+
// Update the loop iterator to the next unprocessed block.
167+
nextPos = SILFunction::iterator(&*std::prev(beforePred));
168+
}
169+
}
170+
100171
//===----------------------------------------------------------------------===//
101172
// SILGenFunction emitStmt implementation
102173
//===----------------------------------------------------------------------===//

lib/SILOptimizer/Utils/CFG.cpp

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "swift/SIL/BasicBlockUtils.h"
1314
#include "swift/SIL/Dominance.h"
1415
#include "swift/SIL/LoopInfo.h"
1516
#include "swift/SIL/SILArgument.h"
@@ -740,16 +741,6 @@ bool swift::mergeBasicBlockWithSuccessor(SILBasicBlock *BB, DominanceInfo *DT,
740741
if (BB == SuccBB || !SuccBB->getSinglePredecessorBlock())
741742
return false;
742743

743-
// If there are any BB arguments in the destination, replace them with the
744-
// branch operands, since they must dominate the dest block.
745-
for (unsigned i = 0, e = Branch->getArgs().size(); i != e; ++i)
746-
SuccBB->getArgument(i)->replaceAllUsesWith(Branch->getArg(i));
747-
748-
Branch->eraseFromParent();
749-
750-
// Move the instruction from the successor block to the current block.
751-
BB->spliceAtEnd(SuccBB);
752-
753744
if (DT)
754745
if (auto *SuccBBNode = DT->getNode(SuccBB)) {
755746
// Change the immediate dominator for children of the successor to be the
@@ -766,7 +757,7 @@ bool swift::mergeBasicBlockWithSuccessor(SILBasicBlock *BB, DominanceInfo *DT,
766757
if (LI)
767758
LI->removeBlock(SuccBB);
768759

769-
SuccBB->eraseFromParent();
760+
mergeBasicBlockWithSingleSuccessor(BB, SuccBB);
770761

771762
return true;
772763
}

0 commit comments

Comments
 (0)