Skip to content

Commit 57559be

Browse files
committed
[LexicalDestroyHoisting] Adopt iterative dataflow.
Adopt IterativeBackwardReachability. Enables hoisting destroy_values over loops. rdar://93369506
1 parent 45ac012 commit 57559be

File tree

2 files changed

+58
-79
lines changed

2 files changed

+58
-79
lines changed

lib/SILOptimizer/Utils/LexicalDestroyHoisting.cpp

Lines changed: 57 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ struct Context final {
4343
/// value->getDefiningInstruction()
4444
SILInstruction *const definition;
4545

46+
SILBasicBlock *defBlock;
47+
4648
SILFunction &function;
4749

4850
InstructionDeleter &deleter;
4951

5052
Context(SILValue const &value, SILFunction &function,
5153
InstructionDeleter &deleter)
5254
: value(value), definition(value->getDefiningInstruction()),
53-
function(function), deleter(deleter) {
55+
defBlock(value->getParentBlock()), function(function),
56+
deleter(deleter) {
5457
assert(value->isLexical());
5558
assert(value->getOwnershipKind() == OwnershipKind::Owned);
5659
}
@@ -63,7 +66,7 @@ struct Usage final {
6366
/// Instructions which are users of the simple (i.e. not reborrowed) value.
6467
SmallPtrSet<SILInstruction *, 16> users;
6568
// The instructions from which the hoisting starts, the destroy_values.
66-
llvm::SmallSetVector<SILInstruction *, 4> ends;
69+
llvm::SmallVector<SILInstruction *, 4> ends;
6770

6871
Usage(){};
6972
Usage(Usage const &) = delete;
@@ -85,7 +88,7 @@ bool findUsage(Context const &context, Usage &usage) {
8588
// flow and determine whether any were reused. They aren't uses over which
8689
// we can't hoist though.
8790
if (isa<DestroyValueInst>(use->getUser())) {
88-
usage.ends.insert(use->getUser());
91+
usage.ends.push_back(use->getUser());
8992
} else {
9093
usage.users.insert(use->getUser());
9194
}
@@ -95,78 +98,71 @@ bool findUsage(Context const &context, Usage &usage) {
9598

9699
/// How destroy_value hoisting is obstructed.
97100
struct DeinitBarriers final {
98-
/// Blocks up to "before the beginning" of which hoisting was able to proceed.
99-
BasicBlockSetVector hoistingReachesBeginBlocks;
100-
101-
/// Blocks to "after the end" of which hoisting was able to proceed.
102-
BasicBlockSet hoistingReachesEndBlocks;
103-
104101
/// Instructions above which destroy_values cannot be hoisted.
105-
SmallVector<SILInstruction *, 4> barriers;
102+
SmallVector<SILInstruction *, 4> instructions;
106103

107104
/// Blocks one of whose phis is a barrier and consequently out of which
108105
/// destroy_values cannot be hoisted.
109-
SmallVector<SILBasicBlock *, 4> phiBarriers;
106+
SmallVector<SILBasicBlock *, 4> phis;
107+
108+
SmallVector<SILBasicBlock *, 4> blocks;
110109

111-
DeinitBarriers(Context &context)
112-
: hoistingReachesBeginBlocks(&context.function),
113-
hoistingReachesEndBlocks(&context.function) {}
110+
DeinitBarriers(Context &context) {}
114111
DeinitBarriers(DeinitBarriers const &) = delete;
115112
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
116113
};
117114

118115
/// Works backwards from the current location of destroy_values to the earliest
119116
/// place they can be hoisted to.
120117
///
121-
/// Implements BackwardReachability::BlockReachability.
118+
/// Implements IterativeBackwardReachability::Effects
119+
/// Implements IterativeBackwardReachability::bindBarriers::Visitor
122120
class Dataflow final {
121+
using Reachability = IterativeBackwardReachability<Dataflow>;
122+
using Effect = Reachability::Effect;
123123
Context const &context;
124124
Usage const &uses;
125-
DeinitBarriers &result;
125+
DeinitBarriers &barriers;
126+
Reachability::Result result;
127+
Reachability reachability;
126128

127129
enum class Classification { Barrier, Other };
128130

129-
BackwardReachability<Dataflow> reachability;
130-
131131
public:
132-
Dataflow(Context const &context, Usage const &uses, DeinitBarriers &result)
133-
: context(context), uses(uses), result(result),
134-
reachability(&context.function, *this) {
135-
// Seed reachability with the scope ending uses from which the backwards
136-
// data flow will begin.
137-
for (auto *end : uses.ends) {
138-
reachability.initLastUse(end);
139-
}
140-
}
132+
Dataflow(Context const &context, Usage const &uses, DeinitBarriers &barriers)
133+
: context(context), uses(uses), barriers(barriers),
134+
result(&context.function),
135+
reachability(&context.function, context.defBlock, *this, result) {}
141136
Dataflow(Dataflow const &) = delete;
142137
Dataflow &operator=(Dataflow const &) = delete;
143138

144-
void run() { reachability.solveBackward(); }
139+
void run();
145140

146141
private:
147-
friend class BackwardReachability<Dataflow>;
142+
friend Reachability;
148143

149-
bool hasReachableBegin(SILBasicBlock *block) {
150-
return result.hoistingReachesBeginBlocks.contains(block);
151-
}
144+
Classification classifyInstruction(SILInstruction *);
152145

153-
void markReachableBegin(SILBasicBlock *block) {
154-
result.hoistingReachesBeginBlocks.insert(block);
155-
}
146+
bool classificationIsBarrier(Classification);
156147

157-
void markReachableEnd(SILBasicBlock *block) {
158-
result.hoistingReachesEndBlocks.insert(block);
159-
}
148+
/// IterativeBackwardReachability::Effects
160149

161-
Classification classifyInstruction(SILInstruction *);
150+
ArrayRef<SILInstruction *> gens() { return uses.ends; }
162151

163-
bool classificationIsBarrier(Classification);
152+
Effect effectForInstruction(SILInstruction *);
153+
Effect effectForPhi(SILBasicBlock *);
164154

165-
void visitedInstruction(SILInstruction *, Classification);
155+
/// IterativeBackwardReachability::bindBarriers::Visitor
166156

167-
bool checkReachableBarrier(SILInstruction *);
157+
void visitBarrierInstruction(SILInstruction *instruction) {
158+
barriers.instructions.push_back(instruction);
159+
}
168160

169-
bool checkReachablePhiBarrier(SILBasicBlock *);
161+
void visitBarrierPhi(SILBasicBlock *block) { barriers.phis.push_back(block); }
162+
163+
void visitBarrierBlock(SILBasicBlock *block) {
164+
barriers.blocks.push_back(block);
165+
}
170166
};
171167

172168
Dataflow::Classification
@@ -193,26 +189,15 @@ bool Dataflow::classificationIsBarrier(Classification classification) {
193189
llvm_unreachable("exhaustive switch not exhaustive?!");
194190
}
195191

196-
void Dataflow::visitedInstruction(SILInstruction *instruction,
197-
Classification classification) {
198-
assert(classifyInstruction(instruction) == classification);
199-
switch (classification) {
200-
case Classification::Barrier:
201-
result.barriers.push_back(instruction);
202-
return;
203-
case Classification::Other:
204-
return;
205-
}
206-
llvm_unreachable("exhaustive switch not exhaustive?!");
207-
}
208-
209-
bool Dataflow::checkReachableBarrier(SILInstruction *instruction) {
192+
Dataflow::Effect Dataflow::effectForInstruction(SILInstruction *instruction) {
193+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
194+
return Effect::Gen();
210195
auto classification = classifyInstruction(instruction);
211-
visitedInstruction(instruction, classification);
212-
return classificationIsBarrier(classification);
196+
return classificationIsBarrier(classification) ? Effect::Kill()
197+
: Effect::NoEffect();
213198
}
214199

215-
bool Dataflow::checkReachablePhiBarrier(SILBasicBlock *block) {
200+
Dataflow::Effect Dataflow::effectForPhi(SILBasicBlock *block) {
216201
assert(llvm::all_of(block->getArguments(),
217202
[&](auto argument) { return PhiValue(argument); }));
218203

@@ -221,10 +206,13 @@ bool Dataflow::checkReachablePhiBarrier(SILBasicBlock *block) {
221206
return classificationIsBarrier(
222207
classifyInstruction(predecessor->getTerminator()));
223208
});
224-
if (isBarrier) {
225-
result.phiBarriers.push_back(block);
226-
}
227-
return isBarrier;
209+
return isBarrier ? Effect::Kill() : Effect::NoEffect();
210+
}
211+
212+
void Dataflow::run() {
213+
reachability.initialize();
214+
reachability.solve();
215+
reachability.findBarriers(*this);
228216
}
229217

230218
/// Hoist the destroy_values of %value.
@@ -256,7 +244,7 @@ bool Rewriter::run() {
256244
//
257245
// A block is a phi barrier iff any of its predecessors' terminators get
258246
// classified as barriers.
259-
for (auto *block : barriers.phiBarriers) {
247+
for (auto *block : barriers.phis) {
260248
madeChange |= createDestroyValue(&block->front());
261249
}
262250

@@ -271,13 +259,9 @@ bool Rewriter::run() {
271259
// have returned true for P, so none of its instructions would ever have been
272260
// classified (except for via checkReachablePhiBarrier, which doesn't record
273261
// terminator barriers).
274-
for (auto instruction : barriers.barriers) {
262+
for (auto instruction : barriers.instructions) {
275263
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
276264
auto successors = terminator->getParentBlock()->getSuccessorBlocks();
277-
// In order for the instruction to have been classified as a barrier,
278-
// reachability would have had to reach the block containing it.
279-
assert(barriers.hoistingReachesEndBlocks.contains(
280-
terminator->getParentBlock()));
281265
for (auto *successor : successors) {
282266
madeChange |= createDestroyValue(&successor->front());
283267
}
@@ -301,12 +285,8 @@ bool Rewriter::run() {
301285
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
302286
//
303287
// control-flow-boundary(B) := beginning-reachable(B) && !end-reachable(P)
304-
for (auto *block : barriers.hoistingReachesBeginBlocks) {
305-
if (auto *predecessor = block->getSinglePredecessorBlock()) {
306-
if (!barriers.hoistingReachesEndBlocks.contains(predecessor)) {
307-
madeChange |= createDestroyValue(&block->front());
308-
}
309-
}
288+
for (auto *block : barriers.blocks) {
289+
madeChange |= createDestroyValue(&block->front());
310290
}
311291

312292
if (madeChange) {
@@ -324,7 +304,7 @@ bool Rewriter::run() {
324304

325305
bool Rewriter::createDestroyValue(SILInstruction *insertionPoint) {
326306
if (auto *ebi = dyn_cast<DestroyValueInst>(insertionPoint)) {
327-
if (uses.ends.contains(insertionPoint)) {
307+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
328308
reusedDestroyValueInsts.insert(insertionPoint);
329309
return false;
330310
}

test/SILOptimizer/lexical_destroy_hoisting.sil

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,11 @@ exit(%thing : @owned $C):
237237

238238
// Don't hoist over loop without uses.
239239
//
240-
// TODO: Eventually, we should hoist over such loops.
241240
// CHECK-LABEL: sil [ossa] @hoist_over_loop_1 : {{.*}} {
242241
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C):
243242
// CHECK: [[CALLEE_GUARANTEED:%[^,]+]] = function_ref @callee_guaranteed
244243
// CHECK: apply [[CALLEE_GUARANTEED]]([[INSTANCE]])
244+
// CHECK: destroy_value [[INSTANCE]]
245245
// CHECK: br [[LOOP_HEADER:bb[0-9]+]]
246246
// CHECK: [[LOOP_HEADER]]:
247247
// CHECK: br [[LOOP_BODY:bb[0-9]+]]
@@ -252,7 +252,6 @@ exit(%thing : @owned $C):
252252
// CHECK: [[LOOP_BACKEDGE]]:
253253
// CHECK: br [[LOOP_HEADER]]
254254
// CHECK: [[EXIT]]:
255-
// CHECK: destroy_value [[INSTANCE]]
256255
// CHECK-LABEL: } // end sil function 'hoist_over_loop_1'
257256
sil [ossa] @hoist_over_loop_1 : $@convention(thin) (@owned C) -> () {
258257
entry(%instance: @owned $C):

0 commit comments

Comments
 (0)