Skip to content

Commit 3e532b2

Browse files
committed
Add a general BackwardReachability analysis.
Pessimistic, non-iterative data flow for analyzing backward reachability from a set of last uses to their dominating def or nearest barrier. Meet: ReachableEnd(predecessor) = intersection(ReachableBegin, successors) Intended for frequently called utilities where minimizing the cost of data flow is more important than analyzing reachability across loops. Expected to visit very few blocks because barriers often occur close to a last use. Note: this does not require initializing bitsets for all blocks in the function for each SSA value being analyzed.
1 parent 0dd0ba6 commit 3e532b2

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===--- Reachability.h ---------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// Reachability data flow analysis using a path-discovery worklist. For
14+
/// efficient data flow propagation based on a single SSA value and its uses.
15+
///
16+
/// TODO: Add an optimistic data flow for more aggresive optimization:
17+
/// - Add another set for blocks reachable by barriers
18+
/// - Change the meet operation to a union
19+
/// - Propagate past barriers up to the SSA def
20+
/// - Iterate to a fix-point.
21+
///
22+
//===----------------------------------------------------------------------===//
23+
24+
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H
25+
#define SWIFT_SILOPTIMIZER_ANALYSIS_REACHABILITY_H
26+
27+
#include "swift/SIL/BasicBlockDatastructures.h"
28+
#include "swift/SIL/SILBasicBlock.h"
29+
30+
namespace swift {
31+
32+
/// Pessimistic, non-iterative data flow for analyzing backward reachability
33+
/// from a set of last uses to their dominating def or nearest barrier.
34+
///
35+
/// Intended for frequently called utilities where minimizing the cost of data
36+
/// flow is more important than analyzing reachability across loops. Expected to
37+
/// visit very few blocks because barriers often occur close to a last use.
38+
///
39+
/// BlockReachability {
40+
/// // True if the beginning of \p block is reachable.
41+
/// // Typically a BasicBlockSet wrapper.
42+
/// bool hasReachableBegin(SILBasicBlock *block);
43+
///
44+
/// // Mark the beginning of a block reachable. Only called once per block.
45+
/// // Typically a BasicBlockSet wrapper.
46+
/// boid markReachableBegin(SILBasicBlock *block);
47+
///
48+
/// // Mark the end of a block reachable. Only called once per block.
49+
/// // Typically a BasicBlockSet wrapper.
50+
/// void markReachableEnd(SILBasicBlock *block);
51+
///
52+
/// // Return true if \p inst is a barrier. Called once for each reachable
53+
/// // instruction, assuming that each lastUse is itself a barrier.
54+
/// // Used by the data flow client to perform additional book-keeping,
55+
/// // such as recording debug_value instructions.
56+
/// bool checkReachableBarrier(SILInstruction *inst);
57+
/// };
58+
template <typename BlockReachability>
59+
class BackwardReachability {
60+
SILFunction *function;
61+
BlockReachability &reachableBlocks;
62+
BasicBlockWorklist cfgWorklist;
63+
64+
public:
65+
BackwardReachability(SILFunction *function,
66+
BlockReachability &reachableBlocks)
67+
: function(function), reachableBlocks(reachableBlocks),
68+
cfgWorklist(function) {}
69+
70+
// Initialize data flow starting points before running solveBackward.
71+
void initLastUse(SILInstruction *lastUsePoint) {
72+
auto *lastUseBlock = lastUsePoint->getParent();
73+
if (canReachBlockBegin(lastUsePoint)) {
74+
pushPreds(lastUseBlock);
75+
}
76+
}
77+
78+
// Data flow "meet": interesection of successor reachability.
79+
void solveBackward() {
80+
while (SILBasicBlock *block = cfgWorklist.popAndForget()) {
81+
if (!meetOverSuccessors(block))
82+
continue;
83+
84+
reachableBlocks.markReachableEnd(block);
85+
86+
if (canReachBlockBegin(block->getTerminator())) {
87+
pushPreds(block);
88+
}
89+
}
90+
}
91+
92+
protected:
93+
BackwardReachability(BackwardReachability const &) = delete;
94+
BackwardReachability &operator=(BackwardReachability const &) = delete;
95+
96+
// Perform a "meet" over successor begin reachability.
97+
// Return true if \p predecessor's end is pessimistically reachable.
98+
//
99+
// Meet:
100+
// ReachableEnd(predecessor) := intersection(ReachableBegin, successors)
101+
bool meetOverSuccessors(SILBasicBlock *block) {
102+
return llvm::all_of(block->getSuccessorBlocks(), [this](auto *successor) {
103+
return reachableBlocks.hasReachableBegin(successor);
104+
});
105+
}
106+
107+
// Local data flow. Computes the block's flow function.
108+
bool canReachBlockBegin(SILInstruction *lastReachablePoint) {
109+
do {
110+
if (reachableBlocks.checkReachableBarrier(lastReachablePoint))
111+
return false;
112+
lastReachablePoint = lastReachablePoint->getPreviousInstruction();
113+
} while (lastReachablePoint);
114+
return true;
115+
}
116+
117+
// Propagate global data flow from \p succBB to its predecessors.
118+
void pushPreds(SILBasicBlock *succBB) {
119+
reachableBlocks.markReachableBegin(succBB);
120+
121+
for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) {
122+
cfgWorklist.pushIfNotVisited(predBB);
123+
}
124+
}
125+
};
126+
127+
} // end namespace swift
128+
129+
#endif

0 commit comments

Comments
 (0)