Skip to content

Commit c3fed98

Browse files
authored
Merge pull request #19886 from atrick/silgen-critedge
SILGen: simplify cleanups and avoid critical edges.
2 parents d60af18 + a03d7bf commit c3fed98

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+438
-563
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

include/swift/SIL/SILBuilder.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,20 @@ class SILBuilder {
173173
/// location.
174174
///
175175
/// Clients should prefer this constructor.
176-
SILBuilder(SILInstruction *I, SILBuilderContext &C)
176+
SILBuilder(SILInstruction *I, const SILDebugScope *DS, SILBuilderContext &C)
177177
: TempContext(C.getModule()), C(C), F(I->getFunction()) {
178+
assert(DS && "instruction has no debug scope");
179+
setCurrentDebugScope(DS);
178180
setInsertionPoint(I);
179181
}
180182

183+
SILBuilder(SILBasicBlock *BB, const SILDebugScope *DS, SILBuilderContext &C)
184+
: TempContext(C.getModule()), C(C), F(BB->getParent()) {
185+
assert(DS && "block has no debug scope");
186+
setCurrentDebugScope(DS);
187+
setInsertionPoint(BB);
188+
}
189+
181190
// Allow a pass to override the current SIL module conventions. This should
182191
// only be done by a pass responsible for lowering SIL to a new stage
183192
// (e.g. AddressLowering).
@@ -2123,10 +2132,8 @@ class SILBuilderWithScope : public SILBuilder {
21232132
///
21242133
/// Clients should prefer this constructor.
21252134
SILBuilderWithScope(SILInstruction *I, SILBuilderContext &C)
2126-
: SILBuilder(I, C) {
2127-
assert(I->getDebugScope() && "instruction has no debug scope");
2128-
setCurrentDebugScope(I->getDebugScope());
2129-
}
2135+
: SILBuilder(I, I->getDebugScope(), C)
2136+
{}
21302137

21312138
explicit SILBuilderWithScope(
21322139
SILInstruction *I,

include/swift/SIL/SILCloner.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,10 @@ void SILCloner<ImplClass>::visitBlocksDepthFirst(
614614
assert(preorderBlocks.empty());
615615

616616
// First clone the CFG region.
617+
//
618+
// FIXME: Add reverse iteration to SILSuccessor, then convert this to an RPOT
619+
// traversal. We would prefer to keep CFG regions in RPO order, and this would
620+
// be more scalable for functions with many large switches.
617621
SmallVector<SILBasicBlock *, 8> dfsWorklist(1, startBB);
618622
while (!dfsWorklist.empty()) {
619623
auto *BB = dfsWorklist.pop_back_val();

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/Cleanup.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,13 @@ void CleanupManager::emitCleanupsForReturn(CleanupLocation loc,
154154
}
155155

156156
/// Emit a new block that jumps to the specified location and runs necessary
157-
/// cleanups based on its level. If there are no cleanups to run, this just
158-
/// returns the dest block.
157+
/// cleanups based on its level. Emit a block even if there are no cleanups;
158+
/// this is usually the destination of a conditional branch, so jumping
159+
/// straight to `dest` creates a critical edge.
159160
SILBasicBlock *CleanupManager::emitBlockForCleanups(JumpDest dest,
160161
SILLocation branchLoc,
161162
ArrayRef<SILValue> args,
162163
ForUnwind_t forUnwind) {
163-
// If there are no cleanups to run, just return the Dest block directly.
164-
if (!hasAnyActiveCleanups(dest.getDepth()))
165-
return dest.getBlock();
166-
167164
// Otherwise, create and emit a new block.
168165
auto *newBlock = SGF.createBasicBlock();
169166
SILGenSavedInsertionPoint IPRAII(SGF, newBlock);

lib/SILGen/Condition.cpp

Lines changed: 13 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,9 @@
1919
using namespace swift;
2020
using namespace Lowering;
2121

22-
void Condition::enterTrue(SILGenFunction &SGF) {
23-
assert(TrueBB && "Cannot call enterTrue without a True block!");
24-
25-
// TrueBB has already been inserted somewhere unless there's a
26-
// continuation block.
27-
if (!ContBB) return;
28-
29-
SGF.B.emitBlock(TrueBB);
22+
void Condition::enter(SILGenFunction &SGF, SILBasicBlock *destBB) {
23+
assert(destBB && "Cannot reenter a finished block.");
24+
SGF.B.emitBlock(destBB);
3025
}
3126

3227
/// Extract the last SILLocation used in BB.
@@ -36,110 +31,24 @@ static SILLocation getContinuationLoc(SILBasicBlock &BB, SILLocation Fallback) {
3631
return L;
3732
return Fallback;
3833
}
39-
void Condition::exitTrue(SILGenFunction &SGF, ArrayRef<SILValue> Args) {
40-
// If there's no continuation block, it's because the condition was
41-
// folded to true. In that case, we just continue emitting code as
42-
// if we were still in the true case, and we're unreachable iff the
43-
// end of the true case is unreachable. In other words, there's
44-
// nothing to do.
45-
if (!ContBB) {
46-
assert(!FalseBB && "no continuation");
47-
return;
48-
}
49-
50-
// If there is a continuation block, we should branch to it if the
51-
// current point is reachable.
52-
if (!SGF.B.hasValidInsertionPoint()) {
53-
// If there is no false code, the continuation block has a use
54-
// because the main condition jumps directly to it.
55-
assert(ContBB->pred_empty() || !FalseBB);
34+
void Condition::exit(SILGenFunction &SGF, SILBasicBlock *destBB,
35+
ArrayRef<SILValue> Args) {
36+
// If the current point it reachable, branch to the continuation block.
37+
if (!SGF.B.hasValidInsertionPoint())
5638
return;
57-
}
5839

59-
// Otherwise, resume into the continuation block. This branch might
60-
// be folded by exitFalse if it turns out that that point is
61-
// unreachable.
6240
SGF.B.createBranch(getContinuationLoc(*SGF.B.getInsertionBB(), Loc),
6341
ContBB, Args);
64-
65-
// Coming out of exitTrue, we can be in one of three states:
66-
// - a valid non-terminal IP, but only if there is no continuation
67-
// block, which is only possible if there is no false block;
68-
// - a valid terminal IP, if the end of the true block was reachable; or
69-
// - a cleared IP, if the end of the true block was not reachable.
70-
}
71-
72-
void Condition::enterFalse(SILGenFunction &SGF) {
73-
assert(FalseBB && "entering the false branch when it was not valid");
74-
75-
// FalseBB has already been inserted somewhere unless there's a
76-
// continuation block.
77-
if (!ContBB) return;
78-
79-
// It's possible to have no insertion point here if the end of the
80-
// true case was unreachable.
81-
SGF.B.emitBlock(FalseBB);
82-
}
83-
84-
void Condition::exitFalse(SILGenFunction &SGF, ArrayRef<SILValue> Args) {
85-
// If there's no continuation block, it's because the condition was
86-
// folded to false. In that case, we just continue emitting code as
87-
// if we were still in the false case, and we're unreachable iff the
88-
// end of the false case is unreachable. In other words, there's
89-
// nothing to do.
90-
if (!ContBB) return;
91-
92-
if (ContBB->pred_empty()) {
93-
// If the true case didn't need the continuation block, then
94-
// we don't either, regardless of whether the current location
95-
// is reachable. Just keep inserting / being unreachable
96-
// right where we are.
97-
} else if (!SGF.B.hasValidInsertionPoint()) {
98-
// If the true case did need the continuation block, but the false
99-
// case doesn't, just merge the continuation block back into its
100-
// single predecessor and move the IP there.
101-
//
102-
// Note that doing this tends to strand the false code after
103-
// everything else in the function, so maybe it's not a great idea.
104-
auto PI = ContBB->pred_begin();
105-
SILBasicBlock *ContPred = *PI;
106-
107-
// Verify there was only a single predecessor to ContBB.
108-
++PI;
109-
assert(PI == ContBB->pred_end() && "Only expect one branch to the ContBB");
110-
111-
// Insert before the uncond branch and zap it.
112-
auto *Br = cast<BranchInst>(ContPred->getTerminator());
113-
SGF.B.setInsertionPoint(Br->getParent());
114-
115-
Br->eraseFromParent();
116-
assert(ContBB->pred_empty() &&
117-
"Zapping the branch should make ContBB dead");
118-
} else {
119-
// Otherwise, branch to the continuation block and start inserting there.
120-
SGF.B.createBranch(getContinuationLoc(*SGF.B.getInsertionBB(), Loc),
121-
ContBB, Args);
122-
}
12342
}
12443

12544
SILBasicBlock *Condition::complete(SILGenFunction &SGF) {
126-
// If there is no continuation block, it's because we
127-
// constant-folded the branch. The case-exit will have left us in a
128-
// normal insertion state (i.e. not a post-terminator IP) with
129-
// nothing to clean up after.
130-
if (!ContBB) {
131-
return SGF.B.getInsertionBB();
45+
assert(!TrueBB && "enterTrue is always called.");
46+
if (FalseBB) {
47+
assert(ContBB->getNumArguments() == 0 &&
48+
"block arguments require a non-empty false path.");
49+
SILGenBuilder(SGF.B, FalseBB).createBranch(Loc, ContBB);
50+
FalseBB = nullptr;
13251
}
133-
134-
// Kill the continuation block if it's not being used. Case-exits
135-
// only leave themselves post-terminator if they use the
136-
// continuation block, so we're in an acceptable insertion state.
137-
if (ContBB->pred_empty() && ContBB->args_empty()) {
138-
SGF.eraseBasicBlock(ContBB);
139-
return SGF.B.hasValidInsertionPoint() ? SGF.B.getInsertionBB() : nullptr;
140-
}
141-
142-
// Okay, we need to insert the continuation block.
14352
SGF.B.emitBlock(ContBB);
14453
return ContBB;
14554
}

lib/SILGen/Condition.h

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ namespace Lowering {
3232

3333
/// A condition is the result of evaluating a boolean expression as
3434
/// control flow.
35+
///
36+
/// For each Condition instance, `enterTrue` must be called before `complete`.
37+
/// If `enterFalse` is skipped, then an empty fall-through block is created.
3538
class LLVM_LIBRARY_VISIBILITY Condition {
36-
/// The blocks responsible for executing the true and false conditions. A
37-
/// block is non-null if that branch is possible, but it's only an independent
38-
/// block if both branches are possible.
39+
/// The blocks responsible for executing the true and false conditions. These
40+
/// are initialized non-null and set to null after being emitted.
3941
SILBasicBlock *TrueBB;
4042
SILBasicBlock *FalseBB;
4143

@@ -50,30 +52,39 @@ class LLVM_LIBRARY_VISIBILITY Condition {
5052
SILBasicBlock *ContBB,
5153
SILLocation L)
5254
: TrueBB(TrueBB), FalseBB(FalseBB), ContBB(ContBB), Loc(L)
53-
{}
54-
55-
bool hasTrue() const { return TrueBB; }
56-
bool hasFalse() const { return FalseBB; }
57-
58-
/// enterTrue - Begin the emission of the true block. This should only be
59-
/// called if hasTrue() returns true.
60-
void enterTrue(SILGenFunction &SGF);
61-
62-
/// exitTrue - End the emission of the true block. This must be called after
63-
/// enterTrue but before anything else on this Condition.
64-
void exitTrue(SILGenFunction &SGF, ArrayRef<SILValue> Args = {});
65-
66-
/// enterFalse - Begin the emission of the false block. This should only be
67-
/// called if hasFalse() returns true.
68-
void enterFalse(SILGenFunction &SGF);
69-
70-
/// exitFalse - End the emission of the true block. This must be called after
71-
/// enterFalse but before anything else on this Condition.
72-
void exitFalse(SILGenFunction &SGF, ArrayRef<SILValue> Args = {});
73-
55+
{
56+
assert((TrueBB != nullptr && FalseBB != nullptr) &&
57+
"Requires non-null block pointers.");
58+
}
59+
60+
/// enterTrue - Begin the emission of the true block.
61+
void enterTrue(SILGenFunction &SGF) { enter(SGF, TrueBB); }
62+
63+
/// exitTrue - End the emission of the true block.
64+
void exitTrue(SILGenFunction &SGF, ArrayRef<SILValue> Args = {}) {
65+
exit(SGF, TrueBB, Args);
66+
TrueBB = nullptr;
67+
}
68+
69+
/// enterFalse - Begin the emission of the false block.
70+
void enterFalse(SILGenFunction &SGF) { enter(SGF, FalseBB); }
71+
72+
/// exitFalse - End the emission of the true block.
73+
void exitFalse(SILGenFunction &SGF, ArrayRef<SILValue> Args = {}) {
74+
exit(SGF, FalseBB, Args);
75+
FalseBB = nullptr;
76+
}
77+
7478
/// complete - Complete this conditional execution. This should be called
7579
/// only after all other calls on this Condition have been made.
80+
/// This leaves SGF's SILGenBuilder at the continuation block.
7681
SILBasicBlock *complete(SILGenFunction &SGF);
82+
83+
protected:
84+
void enter(SILGenFunction &SGF, SILBasicBlock *destBB);
85+
86+
void exit(SILGenFunction &SGF, SILBasicBlock *destBB,
87+
ArrayRef<SILValue> Args = {});
7788
};
7889

7990
/// A conditional value is one that depends on conditional execution.

lib/SILGen/SILGenBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ class SILGenBuilder : public SILBuilder {
6363
SILBasicBlock::iterator insertInst)
6464
: SILGenBuilder(SGF, &*insertBB, insertInst) {}
6565

66+
// Create a new builder, inheriting the given builder's context and debug
67+
// scope.
68+
SILGenBuilder(SILGenBuilder &builder, SILBasicBlock *insertBB)
69+
: SILBuilder(insertBB, builder.getCurrentDebugScope(),
70+
builder.getBuilderContext()),
71+
SGF(builder.SGF)
72+
{}
73+
6674
SILGenModule &getSILGenModule() const;
6775
SILGenFunction &getSILGenFunction() const { return SGF; }
6876

lib/SILGen/SILGenDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ SILValue SILGenFunction::emitOSVersionRangeCheck(SILLocation loc,
12641264
/// specified JumpDest. The insertion point is left in the block where the
12651265
/// condition has matched and any bound variables are in scope.
12661266
///
1267-
void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FailDest,
1267+
void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FalseDest,
12681268
SILLocation loc,
12691269
ProfileCounter NumTrueTaken,
12701270
ProfileCounter NumFalseTaken) {
@@ -1279,7 +1279,7 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FailDest,
12791279
switch (elt.getKind()) {
12801280
case StmtConditionElement::CK_PatternBinding: {
12811281
InitializationPtr initialization =
1282-
InitializationForPattern(*this, FailDest).visit(elt.getPattern());
1282+
InitializationForPattern(*this, FalseDest).visit(elt.getPattern());
12831283

12841284
// Emit the initial value into the initialization.
12851285
FullExpr Scope(Cleanups, CleanupLocation(elt.getInitializer()));
@@ -1320,8 +1320,8 @@ void SILGenFunction::emitStmtCondition(StmtCondition Cond, JumpDest FailDest,
13201320

13211321
// Just branch on the condition. On failure, we unwind any active cleanups,
13221322
// on success we fall through to a new block.
1323+
auto FailBB = Cleanups.emitBlockForCleanups(FalseDest, loc);
13231324
SILBasicBlock *ContBB = createBasicBlock();
1324-
auto FailBB = Cleanups.emitBlockForCleanups(FailDest, loc);
13251325
B.createCondBranch(booleanTestLoc, booleanTestValue, ContBB, FailBB,
13261326
NumTrueTaken, NumFalseTaken);
13271327

0 commit comments

Comments
 (0)