Skip to content

Commit 1e88e44

Browse files
committed
Add critical edge verification and fix SIL passes.
SIL passes were violating the existing invariant on non-cond-br critical edges in several places. I fixed the places that I could find. Wherever there was a post-pass to "clean up" critical edges, I replaced it with a a call to verification that the critical edges aren't broken in the first place. We still need to eliminate critical edges entirely before enabling ownership SIL.
1 parent 9d2af79 commit 1e88e44

File tree

15 files changed

+149
-94
lines changed

15 files changed

+149
-94
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,11 @@ class SILFunction
883883
/// invariants.
884884
void verify(bool SingleFunction = true) const;
885885

886+
/// Verify that all non-cond-br critical edges have been split.
887+
///
888+
/// This is a fast subset of the checks performed in the SILVerifier.
889+
void verifyCriticalEdges() const;
890+
886891
/// Pretty-print the SILFunction.
887892
void dump(bool Verbose) const;
888893
void dump() const;

include/swift/SILOptimizer/Utils/CFG.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,11 @@ SILBasicBlock *splitBasicBlockAndBranch(SILBuilder &B,
118118
/// \brief Return true if the function has a critical edge, false otherwise.
119119
bool hasCriticalEdges(SILFunction &F, bool OnlyNonCondBr);
120120

121-
/// \brief Split all critical edges in the function updating the dominator tree
122-
/// and loop information (if they are not set to null). If \p OnlyNonCondBr is
123-
/// true this will not split cond_br edges (Only edges which can't carry
124-
/// arguments will be split).
125-
bool splitAllCriticalEdges(SILFunction &F, bool OnlyNonCondBr,
126-
DominanceInfo *DT, SILLoopInfo *LI);
121+
/// \brief Split all critical edges in the given function, updating the
122+
/// dominator tree and loop information if they are provided.
123+
///
124+
/// FIXME: This should never be called! Fix passes that create critical edges.
125+
bool splitAllCriticalEdges(SILFunction &F, DominanceInfo *DT, SILLoopInfo *LI);
127126

128127
/// \brief Split all cond_br critical edges with non-trivial arguments in the
129128
/// function updating the dominator tree and loop information (if they are not

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -343,37 +343,41 @@ class EdgeThreadingCloner : public BaseThreadingCloner {
343343
EdgeThreadingCloner(BranchInst *BI)
344344
: BaseThreadingCloner(*BI->getFunction(),
345345
BI->getDestBB(), nullptr) {
346-
DestBB = createEdgeBlockAndRedirectBranch(BI);
346+
createEdgeBlockAndRedirectBranch(BI);
347347
}
348348

349-
SILBasicBlock *createEdgeBlockAndRedirectBranch(BranchInst *BI) {
349+
void createEdgeBlockAndRedirectBranch(BranchInst *BI) {
350350
auto *Fn = BI->getFunction();
351351
auto *SrcBB = BI->getParent();
352-
auto *DestBB = BI->getDestBB();
353-
auto *EdgeBB = Fn->createBasicBlockAfter(SrcBB);
352+
auto *EdgeBB = BI->getDestBB();
353+
354+
this->DestBB = Fn->createBasicBlockAfter(SrcBB);
354355

355356
// Create block arguments.
356-
for (unsigned ArgIdx : range(DestBB->getNumArguments())) {
357-
auto *DestPHIArg = cast<SILPHIArgument>(DestBB->getArgument(ArgIdx));
357+
for (unsigned ArgIdx : range(EdgeBB->getNumArguments())) {
358+
auto *DestPHIArg = cast<SILPHIArgument>(EdgeBB->getArgument(ArgIdx));
358359
assert(BI->getArg(ArgIdx)->getType() == DestPHIArg->getType() &&
359360
"Types must match");
360-
auto *BlockArg = EdgeBB->createPHIArgument(
361+
auto *BlockArg = DestBB->createPHIArgument(
361362
DestPHIArg->getType(), DestPHIArg->getOwnershipKind());
362363
ValueMap[DestPHIArg] = SILValue(BlockArg);
363364
AvailVals.push_back(std::make_pair(DestPHIArg, BlockArg));
364365
}
365366

366367
// Redirect the branch.
367-
SILBuilderWithScope(BI).createBranch(BI->getLoc(), EdgeBB, BI->getArgs());
368+
SILBuilderWithScope(BI).createBranch(BI->getLoc(), DestBB, BI->getArgs());
368369
BI->eraseFromParent();
369-
return EdgeBB;
370370
}
371371

372372
SILBasicBlock *getEdgeBB() {
373373
// DestBB really is the edge basic block we created to clone instructions
374374
// to.
375375
return DestBB;
376376
}
377+
378+
/// Call this after processing all instructions to fix the control flow
379+
/// graph. The branch cloner may have left critical edges.
380+
bool splitCriticalEdges(DominanceInfo *DT, SILLoopInfo *LI);
377381
};
378382

379383
/// Helper class for cloning of basic blocks.
@@ -414,8 +418,7 @@ class BasicBlockCloner : public BaseThreadingCloner {
414418
/// 'NeedToSplitCriticalEdges' to false if all critical edges are split,
415419
/// otherwise this call will try to split all critical edges.
416420
void updateSSAAfterCloning(BaseThreadingCloner &Cloner, SILBasicBlock *SrcBB,
417-
SILBasicBlock *DestBB,
418-
bool NeedToSplitCriticalEdges = true);
421+
SILBasicBlock *DestBB);
419422

420423
// Helper class that provides a callback that can be used in
421424
// inliners/cloners for collecting new call sites.

lib/SIL/SILVerifier.cpp

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
378378
SILOpenedArchetypesTracker OpenedArchetypes;
379379
SmallVector<StringRef, 16> DebugVars;
380380
const SILInstruction *CurInstruction = nullptr;
381+
const SILArgument *CurArgument = nullptr;
381382
DominanceInfo *Dominance = nullptr;
382383

383384
// Used for dominance checking within a basic block.
@@ -404,12 +405,12 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
404405
if (CurInstruction) {
405406
llvm::dbgs() << "Verifying instruction:\n";
406407
CurInstruction->printInContext(llvm::dbgs());
407-
llvm::dbgs() << "In function:\n";
408-
F.print(llvm::dbgs());
409-
} else {
410-
llvm::dbgs() << "In function:\n";
411-
F.print(llvm::dbgs());
408+
} else if (CurArgument) {
409+
llvm::dbgs() << "Verifying argument:\n";
410+
CurArgument->printInContext(llvm::dbgs());
412411
}
412+
llvm::dbgs() << "In function:\n";
413+
F.print(llvm::dbgs());
413414

414415
// We abort by default because we want to always crash in
415416
// the debugger.
@@ -637,29 +638,49 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
637638
return M.getStage() == SILStage::Raw;
638639
}
639640

640-
void visitSILArgument(SILArgument *arg) {
641-
checkLegalType(arg->getFunction(), arg, nullptr);
642-
checkValueBaseOwnership(arg);
643-
644-
if (isa<SILPHIArgument>(arg) && prohibitAddressBlockArgs()) {
645-
// As a structural SIL property, we disallow address-type block
641+
void visitSILPHIArgument(SILPHIArgument *arg) {
642+
// Verify that the `isPhiArgument` property is sound:
643+
// - Phi arguments come from branches.
644+
// - Non-phi arguments have a single predecessor.
645+
if (arg->isPhiArgument()) {
646+
for (SILBasicBlock *predBB : arg->getParent()->getPredecessorBlocks()) {
647+
auto *TI = predBB->getTerminator();
648+
// FIXME: when critical edges are removed, only allow BranchInst.
649+
require(isa <BranchInst>(TI) || isa<CondBranchInst>(TI),
650+
"All phi argument inputs must be from branches.");
651+
}
652+
} else {
653+
}
654+
if (arg->isPhiArgument() && prohibitAddressBlockArgs()) {
655+
// As a property of well-formed SIL, we disallow address-type block
646656
// arguments. Supporting them would prevent reliably reasoning about the
647657
// underlying storage of memory access. This reasoning is important for
648658
// diagnosing violations of memory access rules and supporting future
649659
// optimizations such as bitfield packing. Address-type block arguments
650660
// also create unnecessary complexity for SIL optimization passes that
651661
// need to reason about memory aliasing.
652-
//
653-
// Note: We could allow non-phi block arguments to be addresses, because
654-
// the address source would still be uniquely recoverable. But then
655-
// we would need to separately ensure that something like begin_access is
656-
// never passed as a block argument before being used by end_access. For
657-
// now, it simpler to have a strict prohibition.
658662
require(!arg->getType().isAddress(),
659663
"Block arguments cannot be addresses");
660664
}
661665
}
662666

667+
void visitSILArgument(SILArgument *arg) {
668+
CurArgument = arg;
669+
checkLegalType(arg->getFunction(), arg, nullptr);
670+
checkValueBaseOwnership(arg);
671+
if (auto *phiArg = dyn_cast<SILPHIArgument>(arg)) {
672+
if (phiArg->isPhiArgument())
673+
visitSILPHIArgument(phiArg);
674+
else {
675+
// A non-phi BlockArgument must have a single predecessor unless it is
676+
// unreachable.
677+
require(arg->getParent()->pred_empty()
678+
|| arg->getParent()->getSinglePredecessorBlock(),
679+
"Non-branch terminator must have a unique successor.");
680+
}
681+
}
682+
}
683+
663684
void visitSILInstruction(SILInstruction *I) {
664685
CurInstruction = I;
665686
OpenedArchetypes.registerOpenedArchetypes(I);
@@ -4574,7 +4595,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
45744595
}
45754596
}
45764597

4577-
void verifyBranches(SILFunction *F) {
4598+
void verifyBranches(const SILFunction *F) {
45784599
// Verify that there is no non_condbr critical edge.
45794600
auto isCriticalEdgePred = [](const TermInst *T, unsigned EdgeIdx) {
45804601
assert(T->getSuccessors().size() > EdgeIdx && "Not enough successors");
@@ -4595,7 +4616,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
45954616
};
45964617

45974618
for (auto &BB : *F) {
4598-
TermInst *TI = BB.getTerminator();
4619+
const TermInst *TI = BB.getTerminator();
45994620
CurInstruction = TI;
46004621

46014622
// Check for non-cond_br critical edges.
@@ -4742,6 +4763,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
47424763
}
47434764

47444765
void visitBasicBlockArguments(SILBasicBlock *BB) {
4766+
CurInstruction = nullptr;
47454767
for (auto argI = BB->args_begin(), argEnd = BB->args_end(); argI != argEnd;
47464768
++argI)
47474769
visitSILArgument(*argI);
@@ -4838,6 +4860,10 @@ void SILFunction::verify(bool SingleFunction) const {
48384860
SILVerifier(*this, SingleFunction).verify();
48394861
}
48404862

4863+
void SILFunction::verifyCriticalEdges() const {
4864+
SILVerifier(*this, /*SingleFunction=*/true).verifyBranches(this);
4865+
}
4866+
48414867
/// Verify that a property descriptor follows invariants.
48424868
void SILProperty::verify(const SILModule &M) const {
48434869
#ifdef NDEBUG

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,9 +2376,8 @@ class SwiftArrayOptPass : public SILFunctionTransform {
23762376
for (auto &HoistableLoopNest : HoistableLoopNests)
23772377
ArrayPropertiesSpecializer(DT, LA, HoistableLoopNest).run();
23782378

2379-
// We might have cloned there might be critical edges that need splitting.
2380-
splitAllCriticalEdges(*getFunction(), true /* only cond_br terminators*/,
2381-
DT, nullptr);
2379+
// Verify that no illegal critical edges were created.
2380+
getFunction()->verifyCriticalEdges();
23822381

23832382
LLVM_DEBUG(getFunction()->viewCFG());
23842383

lib/SILOptimizer/Transforms/ARCCodeMotion.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ class ARCCodeMotion : public SILFunctionTransform {
11611161
//
11621162
// TODO: maybe we can do this lazily or maybe we should disallow SIL passes
11631163
// to create critical edges.
1164-
bool EdgeChanged = splitAllCriticalEdges(*F, false, nullptr, nullptr);
1164+
bool EdgeChanged = splitAllCriticalEdges(*F, nullptr, nullptr);
11651165
if (EdgeChanged)
11661166
POA->invalidateFunction(F);
11671167

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,7 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc,
889889
bool MemoryToRegisters::run() {
890890
bool Changed = false;
891891

892-
Changed = splitAllCriticalEdges(F, true, DT, nullptr);
892+
F.verifyCriticalEdges();
893893

894894
// Compute dominator tree node levels for the function.
895895
DomTreeLevelMap DomTreeLevels;

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -206,14 +206,7 @@ static bool isUsedOutsideOfBlock(SILValue V, SILBasicBlock *BB) {
206206

207207
/// Helper function to perform SSA updates in case of jump threading.
208208
void swift::updateSSAAfterCloning(BaseThreadingCloner &Cloner,
209-
SILBasicBlock *SrcBB, SILBasicBlock *DestBB,
210-
bool NeedToSplitCriticalEdges) {
211-
// We are updating SSA form. This means we need to be able to insert phi
212-
// nodes. To make sure we can do this split all critical edges from
213-
// instructions that don't support block arguments.
214-
if (NeedToSplitCriticalEdges)
215-
splitAllCriticalEdges(*DestBB->getParent(), true, nullptr, nullptr);
216-
209+
SILBasicBlock *SrcBB, SILBasicBlock *DestBB) {
217210
SILSSAUpdater SSAUp;
218211
for (auto AvailValPair : Cloner.AvailVals) {
219212
ValueBase *Inst = AvailValPair.first;
@@ -333,6 +326,7 @@ class ThreadInfo {
333326
// We have copied the threaded block into the edge.
334327
Src = Cloner.getEdgeBB();
335328

329+
// Rewrite the cloned branch to eliminate the non-taken path.
336330
if (auto *CondTerm = dyn_cast<CondBranchInst>(Src->getTerminator())) {
337331
// We know the direction this conditional branch is going to take thread
338332
// it.
@@ -371,14 +365,11 @@ class ThreadInfo {
371365
Builder.createBranch(SEI->getLoc(), ThreadedSuccessorBlock,
372366
ArrayRef<SILValue>());
373367
SEI->eraseFromParent();
374-
375-
// Split the edge from 'Dest' to 'ThreadedSuccessorBlock' it is now
376-
// critical. Doing this here safes us from doing it over the whole
377-
// function in updateSSAAfterCloning because we have split all other
378-
// critical edges earlier.
379-
splitEdgesFromTo(Dest, ThreadedSuccessorBlock, nullptr, nullptr);
380368
}
381-
updateSSAAfterCloning(Cloner, Src, Dest, false);
369+
// After rewriting the cloned branch, split the critical edge.
370+
// This does not currently update DominanceInfo.
371+
Cloner.splitCriticalEdges(nullptr, nullptr);
372+
updateSSAAfterCloning(Cloner, Src, Dest);
382373
}
383374
};
384375

@@ -661,7 +652,7 @@ bool SimplifyCFG::dominatorBasedSimplify(DominanceAnalysis *DA) {
661652
// also required for SSA construction in dominatorBasedSimplifications' jump
662653
// threading. It only splits new critical edges it creates by jump threading.
663654
bool Changed =
664-
EnableJumpThread ? splitAllCriticalEdges(Fn, false, DT, nullptr) : false;
655+
EnableJumpThread ? splitAllCriticalEdges(Fn, DT, nullptr) : false;
665656

666657
unsigned MaxIter = MaxIterationsOfDominatorBasedSimplify;
667658
SmallVector<SILBasicBlock *, 16> BlocksForWorklist;
@@ -1062,10 +1053,12 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
10621053
// destination block into this one, rewriting uses of the BBArgs to use the
10631054
// branch arguments as we go.
10641055
EdgeThreadingCloner Cloner(BI);
1065-
10661056
for (auto &I : *DestBB)
10671057
Cloner.process(&I);
10681058

1059+
// Does not currently update DominanceInfo.
1060+
Cloner.splitCriticalEdges(nullptr, nullptr);
1061+
10691062
// Once all the instructions are copied, we can nuke BI itself. We also add
10701063
// the threaded and edge block to the worklist now that they (likely) can be
10711064
// simplified.
@@ -1291,6 +1284,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
12911284
BI->eraseFromParent();
12921285
removeIfDead(DestBB);
12931286
addToWorklist(BB);
1287+
12941288
return true;
12951289
}
12961290

@@ -2444,6 +2438,9 @@ bool SimplifyCFG::tailDuplicateObjCMethodCallSuccessorBlocks() {
24442438
for (auto &I : *DestBB)
24452439
Cloner.process(&I);
24462440

2441+
// Does not currently update DominanceInfo.
2442+
Cloner.splitCriticalEdges(nullptr, nullptr);
2443+
24472444
updateSSAAfterCloning(Cloner, Cloner.getEdgeBB(), DestBB);
24482445
addToWorklist(Cloner.getEdgeBB());
24492446
}
@@ -2863,9 +2860,7 @@ bool SimplifyCFG::run() {
28632860
if (simplifyBlocks())
28642861
removeUnreachableBlocks(Fn);
28652862
}
2866-
2867-
// Split all critical edges from non cond_br terminators.
2868-
Changed |= splitAllCriticalEdges(Fn, true, nullptr, nullptr);
2863+
Fn.verifyCriticalEdges();
28692864

28702865
// Canonicalize switch_enum instructions.
28712866
Changed |= canonicalizeSwitchEnums();
@@ -3615,9 +3610,11 @@ class SplitCriticalEdges : public SILFunctionTransform {
36153610
void run() override {
36163611
auto &Fn = *getFunction();
36173612

3613+
if (OnlyNonCondBrEdges)
3614+
Fn.verifyCriticalEdges();
3615+
36183616
// Split all critical edges from all or non only cond_br terminators.
3619-
bool Changed =
3620-
splitAllCriticalEdges(Fn, OnlyNonCondBrEdges, nullptr, nullptr);
3617+
bool Changed = splitAllCriticalEdges(Fn, nullptr, nullptr);
36213618

36223619
if (Changed) {
36233620
invalidateAnalysis(SILAnalysis::InvalidationKind::BranchesAndInstructions);

lib/SILOptimizer/Utils/CFG.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -704,22 +704,21 @@ bool swift::hasCriticalEdges(SILFunction &F, bool OnlyNonCondBr) {
704704

705705
/// Split all critical edges in the function updating the dominator tree and
706706
/// loop information (if they are not set to null).
707-
bool swift::splitAllCriticalEdges(SILFunction &F, bool OnlyNonCondBr,
708-
DominanceInfo *DT, SILLoopInfo *LI) {
707+
bool swift::splitAllCriticalEdges(SILFunction &F, DominanceInfo *DT,
708+
SILLoopInfo *LI) {
709709
bool Changed = false;
710710

711711
for (SILBasicBlock &BB : F) {
712-
// Only split critical edges for terminators that don't support block
713-
// arguments.
714-
if (OnlyNonCondBr && isa<CondBranchInst>(BB.getTerminator()))
715-
continue;
716-
717712
if (isa<BranchInst>(BB.getTerminator()))
718713
continue;
719714

720-
for (unsigned Idx = 0, e = BB.getSuccessors().size(); Idx != e; ++Idx)
721-
Changed |=
722-
(splitCriticalEdge(BB.getTerminator(), Idx, DT, LI) != nullptr);
715+
for (unsigned Idx = 0, e = BB.getSuccessors().size(); Idx != e; ++Idx) {
716+
auto *NewBB = splitCriticalEdge(BB.getTerminator(), Idx, DT, LI);
717+
assert(!NewBB
718+
|| isa<CondBranchInst>(BB.getTerminator())
719+
&& "Only cond_br may have a critical edge.");
720+
Changed |= (NewBB != nullptr);
721+
}
723722
}
724723
return Changed;
725724
}

0 commit comments

Comments
 (0)