Skip to content

Commit 94ef0af

Browse files
committed
[SILOpt] Reachability handles phis.
Previously, Reachability assumed that phis were not barriers. Here, handling for barrier phis is added. To that end, a new delegate callback `checkReachableBarrier(PhiValue)` is added. Before marking the beginning of a block as reached (or any of its predecessors), check whether each argument that is a phi is a barrier. If any is, then reachability is done. Implemented the new method in SSADestroyHoisting by splitting apart the classification of an instruction and the work to do in response to visiting an instruction. Then, when visiting a PhiValue, just check whether any of the predecessors terminators are classified as barriers. That way, seeing that they're classified that way doesn't result in noting down that those terminators had been reached (which indeed they will not have been if any of the terminators from which the values are flowing into the phi are barriers).
1 parent 45afec6 commit 94ef0af

File tree

2 files changed

+81
-19
lines changed

2 files changed

+81
-19
lines changed

include/swift/SILOptimizer/Analysis/Reachability.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#include "swift/SIL/BasicBlockDatastructures.h"
2828
#include "swift/SIL/SILBasicBlock.h"
29+
#include "swift/SIL/SILSuccessor.h"
30+
#include "llvm/ADT/STLExtras.h"
2931

3032
namespace swift {
3133

@@ -44,16 +46,19 @@ namespace swift {
4446
/// // Mark the beginning of a block reachable. Only called once per block.
4547
/// // Typically a BasicBlockSet wrapper.
4648
/// void markReachableBegin(SILBasicBlock *block);
47-
///
49+
///
4850
/// // Mark the end of a block reachable. Only called once per block.
4951
/// // Typically a BasicBlockSet wrapper.
5052
/// void markReachableEnd(SILBasicBlock *block);
5153
///
5254
/// // Return true if \p inst is a barrier. Called once for each reachable
53-
/// // instruction, assuming that each lastUse is itself a barrier.
55+
/// // instruction, assuming that each lastUsePoint is itself a barrier.
5456
/// // Used by the data flow client to perform additional book-keeping,
5557
/// // such as recording debug_value instructions.
5658
/// bool checkReachableBarrier(SILInstruction *inst);
59+
///
60+
/// // Return true if any of \p block's phis are barriers.
61+
/// bool checkReachablePhiBarrier(SILBasicBlock *block);
5762
/// };
5863
template <typename BlockReachability>
5964
class BackwardReachability {
@@ -70,7 +75,8 @@ class BackwardReachability {
7075
// Initialize data flow starting points before running solveBackward.
7176
void initLastUse(SILInstruction *lastUsePoint) {
7277
auto *lastUseBlock = lastUsePoint->getParent();
73-
if (canReachBlockBegin(lastUsePoint)) {
78+
auto *previous = lastUsePoint->getPreviousInstruction();
79+
if (!previous || canReachBlockBegin(previous)) {
7480
pushPreds(lastUseBlock);
7581
}
7682
}
@@ -116,6 +122,10 @@ class BackwardReachability {
116122

117123
// Propagate global data flow from \p succBB to its predecessors.
118124
void pushPreds(SILBasicBlock *succBB) {
125+
if (succBB->hasPhi())
126+
if (reachableBlocks.checkReachablePhiBarrier(succBB))
127+
return;
128+
119129
reachableBlocks.markReachableBegin(succBB);
120130

121131
for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) {

lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ class DeinitBarriers {
215215
DeinitBarriers &result;
216216
SILInstruction *storageDefInst = nullptr; // null for function args
217217

218+
enum class Classification { DeadUser, Barrier, Other };
219+
218220
BackwardReachability<DestroyReachability> reachability;
219221

220222
public:
@@ -245,37 +247,87 @@ class DeinitBarriers {
245247
result.destroyReachesEndBlocks.insert(block);
246248
}
247249

248-
bool checkReachableBarrier(SILInstruction *inst);
250+
Classification classifyInstruction(SILInstruction *inst);
251+
252+
bool classificationIsBarrier(Classification classification);
253+
254+
void visitedInstruction(SILInstruction *instruction,
255+
Classification classification);
256+
257+
bool checkReachableBarrier(SILInstruction *);
258+
259+
bool checkReachablePhiBarrier(SILBasicBlock *);
249260

250261
void solveBackward() { reachability.solveBackward(); }
251262
};
252263
};
253264

254-
/// Return true if \p inst is a barrier.
255-
///
256-
/// Called exactly once for each reachable instruction. This is guaranteed to
257-
/// hold as a barrier occurs between any original destroys that are reachable
258-
/// from each. Any path reaching multiple destroys requires initialization,
259-
/// which is a storageUser and therefore a barrier.
260-
bool DeinitBarriers::DestroyReachability::checkReachableBarrier(
261-
SILInstruction *inst) {
265+
DeinitBarriers::DestroyReachability::Classification
266+
DeinitBarriers::DestroyReachability::classifyInstruction(SILInstruction *inst) {
262267
if (knownUses.debugInsts.contains(inst)) {
263-
result.deadUsers.push_back(inst);
264-
return false;
268+
return Classification::DeadUser;
265269
}
266270
if (inst == storageDefInst) {
267271
result.barriers.push_back(inst);
268-
return true;
272+
return Classification::Barrier;
269273
}
270274
if (knownUses.storageUsers.contains(inst)) {
271-
result.barriers.push_back(inst);
272-
return true;
275+
return Classification::Barrier;
273276
}
274277
if (isDeinitBarrier(inst)) {
275-
result.barriers.push_back(inst);
278+
return Classification::Barrier;
279+
}
280+
return Classification::Other;
281+
}
282+
283+
bool DeinitBarriers::DestroyReachability::classificationIsBarrier(
284+
Classification classification) {
285+
switch (classification) {
286+
case Classification::DeadUser:
287+
case Classification::Other:
288+
return false;
289+
case Classification::Barrier:
276290
return true;
277291
}
278-
return false;
292+
llvm_unreachable("exhaustive switch is not exhaustive?!");
293+
}
294+
295+
void DeinitBarriers::DestroyReachability::visitedInstruction(
296+
SILInstruction *instruction, Classification classification) {
297+
assert(classifyInstruction(instruction) == classification);
298+
switch (classification) {
299+
case Classification::DeadUser:
300+
result.deadUsers.push_back(instruction);
301+
break;
302+
case Classification::Barrier:
303+
result.barriers.push_back(instruction);
304+
break;
305+
case Classification::Other:
306+
break;
307+
}
308+
}
309+
310+
/// Return true if \p inst is a barrier.
311+
///
312+
/// Called exactly once for each reachable instruction. This is guaranteed to
313+
/// hold as a barrier occurs between any original destroys that are reachable
314+
/// from each. Any path reaching multiple destroys requires initialization,
315+
/// which is a storageUser and therefore a barrier.
316+
bool DeinitBarriers::DestroyReachability::checkReachableBarrier(
317+
SILInstruction *instruction) {
318+
auto classification = classifyInstruction(instruction);
319+
visitedInstruction(instruction, classification);
320+
return classificationIsBarrier(classification);
321+
}
322+
323+
bool DeinitBarriers::DestroyReachability::checkReachablePhiBarrier(
324+
SILBasicBlock *block) {
325+
assert(llvm::all_of(block->getArguments(),
326+
[&](auto argument) { return PhiValue(argument); }));
327+
return llvm::any_of(block->getPredecessorBlocks(), [&](auto *predecessor) {
328+
return classificationIsBarrier(
329+
classifyInstruction(predecessor->getTerminator()));
330+
});
279331
}
280332

281333
/// Algorithm for hoisting the destroys of a single uniquely identified storage

0 commit comments

Comments
 (0)