Skip to content

Commit 12bb49f

Browse files
committed
Expose SIL/BasicBlockUtils for critical edge splitting.
The primary interfaces for CFG manipulation belong in SIL. This is just what's necessary to fix SILCloner.
1 parent f625f46 commit 12bb49f

File tree

7 files changed

+315
-302
lines changed

7 files changed

+315
-302
lines changed

include/swift/SIL/BasicBlockUtils.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,40 @@
1313
#ifndef SWIFT_SIL_DEADENDBLOCKS_H
1414
#define SWIFT_SIL_DEADENDBLOCKS_H
1515

16+
#include "swift/SIL/SILValue.h"
1617
#include "llvm/ADT/SetVector.h"
18+
#include "llvm/ADT/SmallVector.h"
1719

1820
namespace swift {
1921

2022
class SILFunction;
2123
class SILBasicBlock;
24+
class TermInst;
25+
class DominanceInfo;
26+
class SILLoopInfo;
27+
28+
/// \brief Replace a branch target.
29+
///
30+
/// \param T The terminating instruction to modify.
31+
/// \param edgeIdx The successor edges index that will be replaced.
32+
/// \param newDest The new target block.
33+
/// \param preserveArgs If set, preserve arguments on the replaced edge.
34+
void changeBranchTarget(TermInst *T, unsigned edgeIdx, SILBasicBlock *newDest,
35+
bool preserveArgs);
36+
37+
/// Returns the arguments values on the specified CFG edge. If necessary, may
38+
/// add create new SILPHIArguments, using `NewEdgeBB` as the placeholder.
39+
void getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB,
40+
llvm::SmallVectorImpl<SILValue> &args);
41+
42+
/// \brief Splits the edge from terminator.
43+
///
44+
/// Also updates dominance and loop information if not null.
45+
///
46+
/// Returns the newly created basic block.
47+
SILBasicBlock *splitEdge(TermInst *T, unsigned edgeIdx,
48+
DominanceInfo *DT = nullptr,
49+
SILLoopInfo *LI = nullptr);
2250

2351
/// \brief Merge a basic block ending in a branch with its successor
2452
/// if possible.

include/swift/SILOptimizer/Utils/CFG.h

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

48-
/// \brief Replace a branch target.
49-
///
50-
/// \param T The terminating instruction to modify.
51-
/// \param EdgeIdx The successor edges index that will be replaced.
52-
/// \param NewDest The new target block.
53-
/// \param PreserveArgs If set, preserve arguments on the replaced edge.
54-
void changeBranchTarget(TermInst *T, unsigned EdgeIdx, SILBasicBlock *NewDest,
55-
bool PreserveArgs);
56-
5748
/// \brief Replace a branch target.
5849
///
5950
/// \param T The terminating instruction to modify.
6051
/// \param OldDest The successor block that will be replaced.
6152
/// \param NewDest The new target block.
6253
/// \param PreserveArgs If set, preserve arguments on the replaced edge.
63-
void replaceBranchTarget(TermInst *T, SILBasicBlock *OldDest, SILBasicBlock *NewDest,
64-
bool PreserveArgs);
54+
void replaceBranchTarget(TermInst *T, SILBasicBlock *OldDest,
55+
SILBasicBlock *NewDest, bool PreserveArgs);
6556

6657
/// \brief Check if the edge from the terminator is critical.
6758
bool isCriticalEdge(TermInst *T, unsigned EdgeIdx);
@@ -85,14 +76,6 @@ SILBasicBlock *splitIfCriticalEdge(SILBasicBlock *From, SILBasicBlock *To,
8576
DominanceInfo *DT = nullptr,
8677
SILLoopInfo *LI = nullptr);
8778

88-
/// \brief Splits the edge from terminator.
89-
///
90-
/// Updates dominance information and loop information if not null.
91-
/// Returns the newly created basic block.
92-
SILBasicBlock *splitEdge(TermInst *T, unsigned EdgeIdx,
93-
DominanceInfo *DT = nullptr,
94-
SILLoopInfo *LI = nullptr);
95-
9679
/// \brief Splits the edges between two basic blocks.
9780
///
9881
/// Updates dominance information and loop information if not null.

lib/SIL/BasicBlockUtils.cpp

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,292 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SIL/BasicBlockUtils.h"
14+
#include "swift/SIL/Dominance.h"
15+
#include "swift/SIL/LoopInfo.h"
1416
#include "swift/SIL/SILArgument.h"
1517
#include "swift/SIL/SILBasicBlock.h"
18+
#include "swift/SIL/SILBuilder.h"
1619
#include "swift/SIL/SILFunction.h"
1720

1821
using namespace swift;
1922

23+
static bool hasBranchArguments(TermInst *T, unsigned edgeIdx) {
24+
if (auto *BI = dyn_cast<BranchInst>(T)) {
25+
assert(edgeIdx == 0);
26+
return BI->getNumArgs() != 0;
27+
}
28+
if (auto CBI = dyn_cast<CondBranchInst>(T)) {
29+
assert(edgeIdx <= 1);
30+
return edgeIdx == CondBranchInst::TrueIdx ? !CBI->getTrueArgs().empty()
31+
: !CBI->getFalseArgs().empty();
32+
}
33+
// No other terminator have branch arguments.
34+
return false;
35+
}
36+
37+
void swift::changeBranchTarget(TermInst *T, unsigned edgeIdx,
38+
SILBasicBlock *newDest, bool preserveArgs) {
39+
// In many cases, we can just rewrite the successor in place.
40+
if (preserveArgs || !hasBranchArguments(T, edgeIdx)) {
41+
T->getSuccessors()[edgeIdx] = newDest;
42+
return;
43+
}
44+
45+
// Otherwise, we have to build a new branch instruction.
46+
SILBuilderWithScope B(T);
47+
48+
switch (T->getTermKind()) {
49+
// Only Branch and CondBranch may have arguments.
50+
case TermKind::BranchInst: {
51+
auto *BI = cast<BranchInst>(T);
52+
SmallVector<SILValue, 8> args;
53+
for (auto arg : BI->getArgs())
54+
args.push_back(arg);
55+
B.createBranch(T->getLoc(), newDest, args);
56+
BI->dropAllReferences();
57+
BI->eraseFromParent();
58+
return;
59+
}
60+
61+
case TermKind::CondBranchInst: {
62+
auto CBI = cast<CondBranchInst>(T);
63+
SILBasicBlock *trueDest = CBI->getTrueBB();
64+
SILBasicBlock *falseDest = CBI->getFalseBB();
65+
66+
SmallVector<SILValue, 8> trueArgs;
67+
SmallVector<SILValue, 8> falseArgs;
68+
if (edgeIdx == CondBranchInst::FalseIdx) {
69+
falseDest = newDest;
70+
for (auto arg : CBI->getTrueArgs())
71+
trueArgs.push_back(arg);
72+
} else {
73+
trueDest = newDest;
74+
for (auto arg : CBI->getFalseArgs())
75+
falseArgs.push_back(arg);
76+
}
77+
78+
B.createCondBranch(CBI->getLoc(), CBI->getCondition(), trueDest, trueArgs,
79+
falseDest, falseArgs, CBI->getTrueBBCount(),
80+
CBI->getFalseBBCount());
81+
CBI->dropAllReferences();
82+
CBI->eraseFromParent();
83+
return;
84+
}
85+
86+
default:
87+
llvm_unreachable("only branch and cond_branch have branch arguments");
88+
}
89+
}
90+
91+
template <class SwitchInstTy>
92+
static SILBasicBlock *getNthEdgeBlock(SwitchInstTy *S, unsigned edgeIdx) {
93+
if (S->getNumCases() == edgeIdx)
94+
return S->getDefaultBB();
95+
return S->getCase(edgeIdx).second;
96+
}
97+
98+
void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB,
99+
llvm::SmallVectorImpl<SILValue> &args) {
100+
switch (T->getKind()) {
101+
case SILInstructionKind::BranchInst: {
102+
auto *B = cast<BranchInst>(T);
103+
for (auto V : B->getArgs())
104+
args.push_back(V);
105+
return;
106+
}
107+
108+
case SILInstructionKind::CondBranchInst: {
109+
auto CBI = cast<CondBranchInst>(T);
110+
assert(edgeIdx < 2);
111+
auto OpdArgs = edgeIdx ? CBI->getFalseArgs() : CBI->getTrueArgs();
112+
for (auto V : OpdArgs)
113+
args.push_back(V);
114+
return;
115+
}
116+
117+
case SILInstructionKind::SwitchValueInst: {
118+
auto SEI = cast<SwitchValueInst>(T);
119+
auto *succBB = getNthEdgeBlock(SEI, edgeIdx);
120+
assert(succBB->getNumArguments() == 0 && "Can't take an argument");
121+
(void)succBB;
122+
return;
123+
}
124+
125+
// A switch_enum can implicitly pass the enum payload. We need to look at the
126+
// destination block to figure this out.
127+
case SILInstructionKind::SwitchEnumInst:
128+
case SILInstructionKind::SwitchEnumAddrInst: {
129+
auto SEI = cast<SwitchEnumInstBase>(T);
130+
auto *succBB = getNthEdgeBlock(SEI, edgeIdx);
131+
assert(succBB->getNumArguments() < 2 && "Can take at most one argument");
132+
if (!succBB->getNumArguments())
133+
return;
134+
args.push_back(newEdgeBB->createPhiArgument(
135+
succBB->getArgument(0)->getType(), ValueOwnershipKind::Owned));
136+
return;
137+
}
138+
139+
// A dynamic_method_br passes the function to the first basic block.
140+
case SILInstructionKind::DynamicMethodBranchInst: {
141+
auto DMBI = cast<DynamicMethodBranchInst>(T);
142+
auto *succBB =
143+
(edgeIdx == 0) ? DMBI->getHasMethodBB() : DMBI->getNoMethodBB();
144+
if (!succBB->getNumArguments())
145+
return;
146+
args.push_back(newEdgeBB->createPhiArgument(
147+
succBB->getArgument(0)->getType(), ValueOwnershipKind::Owned));
148+
return;
149+
}
150+
151+
/// A checked_cast_br passes the result of the cast to the first basic block.
152+
case SILInstructionKind::CheckedCastBranchInst: {
153+
auto CBI = cast<CheckedCastBranchInst>(T);
154+
auto succBB = edgeIdx == 0 ? CBI->getSuccessBB() : CBI->getFailureBB();
155+
if (!succBB->getNumArguments())
156+
return;
157+
args.push_back(newEdgeBB->createPhiArgument(
158+
succBB->getArgument(0)->getType(), ValueOwnershipKind::Owned));
159+
return;
160+
}
161+
case SILInstructionKind::CheckedCastAddrBranchInst: {
162+
auto CBI = cast<CheckedCastAddrBranchInst>(T);
163+
auto succBB = edgeIdx == 0 ? CBI->getSuccessBB() : CBI->getFailureBB();
164+
if (!succBB->getNumArguments())
165+
return;
166+
args.push_back(newEdgeBB->createPhiArgument(
167+
succBB->getArgument(0)->getType(), ValueOwnershipKind::Owned));
168+
return;
169+
}
170+
case SILInstructionKind::CheckedCastValueBranchInst: {
171+
auto CBI = cast<CheckedCastValueBranchInst>(T);
172+
auto succBB = edgeIdx == 0 ? CBI->getSuccessBB() : CBI->getFailureBB();
173+
if (!succBB->getNumArguments())
174+
return;
175+
args.push_back(newEdgeBB->createPhiArgument(
176+
succBB->getArgument(0)->getType(), ValueOwnershipKind::Owned));
177+
return;
178+
}
179+
180+
case SILInstructionKind::TryApplyInst: {
181+
auto *TAI = cast<TryApplyInst>(T);
182+
auto *succBB = edgeIdx == 0 ? TAI->getNormalBB() : TAI->getErrorBB();
183+
if (!succBB->getNumArguments())
184+
return;
185+
args.push_back(newEdgeBB->createPhiArgument(
186+
succBB->getArgument(0)->getType(), ValueOwnershipKind::Owned));
187+
return;
188+
}
189+
190+
case SILInstructionKind::YieldInst:
191+
// The edges from 'yield' never have branch arguments.
192+
return;
193+
194+
case SILInstructionKind::ReturnInst:
195+
case SILInstructionKind::ThrowInst:
196+
case SILInstructionKind::UnwindInst:
197+
case SILInstructionKind::UnreachableInst:
198+
llvm_unreachable("terminator never has successors");
199+
200+
#define TERMINATOR(ID, ...)
201+
#define INST(ID, BASE) case SILInstructionKind::ID:
202+
#include "swift/SIL/SILNodes.def"
203+
llvm_unreachable("not a terminator");
204+
}
205+
llvm_unreachable("bad instruction kind");
206+
}
207+
208+
SILBasicBlock *swift::splitEdge(TermInst *T, unsigned edgeIdx,
209+
DominanceInfo *DT, SILLoopInfo *LI) {
210+
auto *srcBB = T->getParent();
211+
auto *F = srcBB->getParent();
212+
213+
SILBasicBlock *destBB = T->getSuccessors()[edgeIdx];
214+
215+
// Create a new basic block in the edge, and insert it after the srcBB.
216+
auto *edgeBB = F->createBasicBlockAfter(srcBB);
217+
218+
SmallVector<SILValue, 16> args;
219+
getEdgeArgs(T, edgeIdx, edgeBB, args);
220+
221+
SILBuilder(edgeBB).createBranch(T->getLoc(), destBB, args);
222+
223+
// Strip the arguments and rewire the branch in the source block.
224+
changeBranchTarget(T, edgeIdx, edgeBB, /*PreserveArgs=*/false);
225+
226+
if (!DT && !LI)
227+
return edgeBB;
228+
229+
// Update the dominator tree.
230+
if (DT) {
231+
auto *srcBBNode = DT->getNode(srcBB);
232+
233+
// Unreachable code could result in a null return here.
234+
if (srcBBNode) {
235+
// The new block is dominated by the srcBB.
236+
auto *edgeBBNode = DT->addNewBlock(edgeBB, srcBB);
237+
238+
// Are all predecessors of destBB dominated by destBB?
239+
auto *destBBNode = DT->getNode(destBB);
240+
bool oldSrcBBDominatesAllPreds = std::all_of(
241+
destBB->pred_begin(), destBB->pred_end(), [=](SILBasicBlock *B) {
242+
if (B == edgeBB)
243+
return true;
244+
auto *PredNode = DT->getNode(B);
245+
if (!PredNode)
246+
return true;
247+
if (DT->dominates(destBBNode, PredNode))
248+
return true;
249+
return false;
250+
});
251+
252+
// If so, the new bb dominates destBB now.
253+
if (oldSrcBBDominatesAllPreds)
254+
DT->changeImmediateDominator(destBBNode, edgeBBNode);
255+
}
256+
}
257+
258+
if (!LI)
259+
return edgeBB;
260+
261+
// Update loop info. Both blocks must be in a loop otherwise the split block
262+
// is outside the loop.
263+
SILLoop *srcBBLoop = LI->getLoopFor(srcBB);
264+
if (!srcBBLoop)
265+
return edgeBB;
266+
SILLoop *DstBBLoop = LI->getLoopFor(destBB);
267+
if (!DstBBLoop)
268+
return edgeBB;
269+
270+
// Same loop.
271+
if (DstBBLoop == srcBBLoop) {
272+
DstBBLoop->addBasicBlockToLoop(edgeBB, LI->getBase());
273+
return edgeBB;
274+
}
275+
276+
// Edge from inner to outer loop.
277+
if (DstBBLoop->contains(srcBBLoop)) {
278+
DstBBLoop->addBasicBlockToLoop(edgeBB, LI->getBase());
279+
return edgeBB;
280+
}
281+
282+
// Edge from outer to inner loop.
283+
if (srcBBLoop->contains(DstBBLoop)) {
284+
srcBBLoop->addBasicBlockToLoop(edgeBB, LI->getBase());
285+
return edgeBB;
286+
}
287+
288+
// Neither loop contains the other. The destination must be the header of its
289+
// loop. Otherwise, we would be creating irreducible control flow.
290+
assert(DstBBLoop->getHeader() == destBB
291+
&& "Creating irreducible control flow?");
292+
293+
// Add to outer loop if there is one.
294+
if (auto *parent = DstBBLoop->getParentLoop())
295+
parent->addBasicBlockToLoop(edgeBB, LI->getBase());
296+
297+
return edgeBB;
298+
}
299+
20300
/// Merge the basic block with its successor if possible.
21301
void swift::mergeBasicBlockWithSingleSuccessor(SILBasicBlock *BB,
22302
SILBasicBlock *succBB) {

lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#define DEBUG_TYPE "sil-speculative-devirtualizer"
1919

20+
#include "swift/SIL/BasicBlockUtils.h"
2021
#include "swift/SIL/SILArgument.h"
2122
#include "swift/SIL/SILBuilder.h"
2223
#include "swift/SIL/SILFunction.h"
@@ -29,7 +30,6 @@
2930
#include "swift/SILOptimizer/PassManager/Passes.h"
3031
#include "swift/SILOptimizer/PassManager/PassManager.h"
3132
#include "swift/SILOptimizer/PassManager/Transforms.h"
32-
#include "swift/SILOptimizer/Utils/CFG.h"
3333
#include "swift/SILOptimizer/Utils/Devirtualize.h"
3434
#include "swift/SILOptimizer/Utils/SILInliner.h"
3535
#include "swift/AST/ASTContext.h"

0 commit comments

Comments
 (0)