Skip to content

Commit c5e93fc

Browse files
committed
[LexicalDestroyHoisting] Adopt iterative dataflow.
Adopt IterativeBackwardReachability. Enables hoisting destroy_values over loops. rdar://93369506
1 parent a089429 commit c5e93fc

File tree

2 files changed

+62
-80
lines changed

2 files changed

+62
-80
lines changed

lib/SILOptimizer/Utils/LexicalDestroyHoisting.cpp

Lines changed: 61 additions & 78 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,80 +98,68 @@ 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;
110107

111-
DeinitBarriers(Context &context)
112-
: hoistingReachesBeginBlocks(&context.function),
113-
hoistingReachesEndBlocks(&context.function) {}
108+
SmallVector<SILBasicBlock *, 4> blocks;
109+
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>;
148-
149-
bool hasReachableBegin(SILBasicBlock *block) {
150-
return result.hoistingReachesBeginBlocks.contains(block);
151-
}
152-
153-
void markReachableBegin(SILBasicBlock *block) {
154-
result.hoistingReachesBeginBlocks.insert(block);
155-
}
156-
157-
void markReachableEnd(SILBasicBlock *block) {
158-
result.hoistingReachesEndBlocks.insert(block);
159-
}
142+
friend Reachability;
160143

161144
Classification classifyInstruction(SILInstruction *);
162145

163146
bool classificationIsBarrier(Classification);
164147

165-
void visitedInstruction(SILInstruction *, Classification);
166-
167-
bool checkReachableBarrier(SILInstruction *);
148+
ArrayRef<SILInstruction *> gens();
149+
Effect effectForInstruction(SILInstruction *);
150+
Effect effectForPhi(SILBasicBlock *);
168151

169-
bool checkReachablePhiBarrier(SILBasicBlock *);
152+
void visitBarrierInstruction(SILInstruction *);
153+
void visitBarrierPhi(SILBasicBlock *);
154+
void visitBarrierBlock(SILBasicBlock *);
170155
};
171156

157+
void Dataflow::run() {
158+
reachability.initialize();
159+
reachability.solve();
160+
reachability.findBarriers(*this);
161+
}
162+
172163
Dataflow::Classification
173164
Dataflow::classifyInstruction(SILInstruction *instruction) {
174165
if (instruction == context.definition) {
@@ -193,26 +184,17 @@ bool Dataflow::classificationIsBarrier(Classification classification) {
193184
llvm_unreachable("exhaustive switch not exhaustive?!");
194185
}
195186

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-
}
187+
ArrayRef<SILInstruction *> Dataflow::gens() { return uses.ends; }
208188

209-
bool Dataflow::checkReachableBarrier(SILInstruction *instruction) {
189+
Dataflow::Effect Dataflow::effectForInstruction(SILInstruction *instruction) {
190+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
191+
return Effect::Gen();
210192
auto classification = classifyInstruction(instruction);
211-
visitedInstruction(instruction, classification);
212-
return classificationIsBarrier(classification);
193+
return classificationIsBarrier(classification) ? Effect::Kill()
194+
: Effect::NoEffect();
213195
}
214196

215-
bool Dataflow::checkReachablePhiBarrier(SILBasicBlock *block) {
197+
Dataflow::Effect Dataflow::effectForPhi(SILBasicBlock *block) {
216198
assert(llvm::all_of(block->getArguments(),
217199
[&](auto argument) { return PhiValue(argument); }));
218200

@@ -221,10 +203,19 @@ bool Dataflow::checkReachablePhiBarrier(SILBasicBlock *block) {
221203
return classificationIsBarrier(
222204
classifyInstruction(predecessor->getTerminator()));
223205
});
224-
if (isBarrier) {
225-
result.phiBarriers.push_back(block);
226-
}
227-
return isBarrier;
206+
return isBarrier ? Effect::Kill() : Effect::NoEffect();
207+
}
208+
209+
void Dataflow::visitBarrierInstruction(SILInstruction *instruction) {
210+
barriers.instructions.push_back(instruction);
211+
}
212+
213+
void Dataflow::visitBarrierPhi(SILBasicBlock *block) {
214+
barriers.phis.push_back(block);
215+
}
216+
217+
void Dataflow::visitBarrierBlock(SILBasicBlock *block) {
218+
barriers.blocks.push_back(block);
228219
}
229220

230221
/// Hoist the destroy_values of %value.
@@ -256,7 +247,7 @@ bool Rewriter::run() {
256247
//
257248
// A block is a phi barrier iff any of its predecessors' terminators get
258249
// classified as barriers.
259-
for (auto *block : barriers.phiBarriers) {
250+
for (auto *block : barriers.phis) {
260251
madeChange |= createDestroyValue(&block->front());
261252
}
262253

@@ -271,13 +262,9 @@ bool Rewriter::run() {
271262
// have returned true for P, so none of its instructions would ever have been
272263
// classified (except for via checkReachablePhiBarrier, which doesn't record
273264
// terminator barriers).
274-
for (auto instruction : barriers.barriers) {
265+
for (auto instruction : barriers.instructions) {
275266
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
276267
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()));
281268
for (auto *successor : successors) {
282269
madeChange |= createDestroyValue(&successor->front());
283270
}
@@ -301,12 +288,8 @@ bool Rewriter::run() {
301288
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
302289
//
303290
// 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-
}
291+
for (auto *block : barriers.blocks) {
292+
madeChange |= createDestroyValue(&block->front());
310293
}
311294

312295
if (madeChange) {
@@ -324,7 +307,7 @@ bool Rewriter::run() {
324307

325308
bool Rewriter::createDestroyValue(SILInstruction *insertionPoint) {
326309
if (auto *ebi = dyn_cast<DestroyValueInst>(insertionPoint)) {
327-
if (uses.ends.contains(insertionPoint)) {
310+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
328311
reusedDestroyValueInsts.insert(insertionPoint);
329312
return false;
330313
}

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)