Skip to content

Commit f1540b1

Browse files
committed
[LexicalDestroyFolding] Adopt iterative dataflow.
Adopt IterativeBackwardReachability. Enables hoisting destroy_values over loops. rdar://93369506
1 parent 2a07376 commit f1540b1

File tree

2 files changed

+46
-78
lines changed

2 files changed

+46
-78
lines changed

lib/SILOptimizer/Utils/LexicalDestroyHoisting.cpp

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,16 @@ 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), deleter(deleter) {
5456
assert(value->isLexical());
5557
assert(value->getOwnershipKind() == OwnershipKind::Owned);
5658
}
@@ -63,7 +65,7 @@ struct Usage final {
6365
/// Instructions which are users of the simple (i.e. not reborrowed) value.
6466
SmallPtrSet<SILInstruction *, 16> users;
6567
// The instructions from which the hoisting starts, the destroy_values.
66-
llvm::SmallSetVector<SILInstruction *, 4> ends;
68+
llvm::SmallVector<SILInstruction *, 4> ends;
6769

6870
Usage(){};
6971
Usage(Usage const &) = delete;
@@ -85,7 +87,7 @@ bool findUsage(Context const &context, Usage &usage) {
8587
// flow and determine whether any were reused. They aren't uses over which
8688
// we can't hoist though.
8789
if (isa<DestroyValueInst>(use->getUser())) {
88-
usage.ends.insert(use->getUser());
90+
usage.ends.push_back(use->getUser());
8991
} else {
9092
usage.users.insert(use->getUser());
9193
}
@@ -95,80 +97,66 @@ bool findUsage(Context const &context, Usage &usage) {
9597

9698
/// How destroy_value hoisting is obstructed.
9799
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-
104100
/// Instructions above which destroy_values cannot be hoisted.
105-
SmallVector<SILInstruction *, 4> barriers;
101+
SmallSetVector<SILInstruction *, 4> instructions;
106102

107103
/// Blocks one of whose phis is a barrier and consequently out of which
108104
/// destroy_values cannot be hoisted.
109-
SmallVector<SILBasicBlock *, 4> phiBarriers;
105+
SmallSetVector<SILBasicBlock *, 4> phis;
106+
107+
SmallSetVector<SILBasicBlock *, 4> blocks;
110108

111-
DeinitBarriers(Context &context)
112-
: hoistingReachesBeginBlocks(&context.function),
113-
hoistingReachesEndBlocks(&context.function) {}
109+
DeinitBarriers(Context &context) {}
114110
DeinitBarriers(DeinitBarriers const &) = delete;
115111
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
116112
};
117113

118114
/// Works backwards from the current location of destroy_values to the earliest
119115
/// place they can be hoisted to.
120116
///
121-
/// Implements BackwardReachability::BlockReachability.
117+
/// Implements IterativeBackwardReachability::Effects
122118
class DataFlow final {
119+
using Reachability = IterativeBackwardReachability<DataFlow>;
120+
using Effect = Reachability::Effect;
123121
Context const &context;
124122
Usage const &uses;
125-
DeinitBarriers &result;
123+
DeinitBarriers &barriers;
124+
Reachability::Result result;
125+
Reachability reachability;
126126

127127
enum class Classification { Barrier, Other };
128128

129-
BackwardReachability<DataFlow> reachability;
130-
131129
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-
}
130+
DataFlow(Context const &context, Usage const &uses, DeinitBarriers &barriers)
131+
: context(context), uses(uses), barriers(barriers), result(&context.function),
132+
reachability(&context.function, context.defBlock, *this, result) {
140133
}
141134
DataFlow(DataFlow const &) = delete;
142135
DataFlow &operator=(DataFlow const &) = delete;
143136

144-
void run() { reachability.solveBackward(); }
137+
void run();
145138

146139
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-
}
140+
friend Reachability;
160141

161142
Classification classifyInstruction(SILInstruction *);
162143

163144
bool classificationIsBarrier(Classification);
164145

165-
void visitedInstruction(SILInstruction *, Classification);
146+
ArrayRef<SILInstruction *> gens();
166147

167-
bool checkReachableBarrier(SILInstruction *);
148+
Optional<Effect> effectForInstruction(SILInstruction *);
168149

169-
bool checkReachablePhiBarrier(SILBasicBlock *);
150+
Optional<Effect> effectForPhi(SILBasicBlock *);
170151
};
171152

153+
void DataFlow::run() {
154+
reachability.initialize();
155+
reachability.solve();
156+
reachability.findBarriers(barriers.instructions, barriers.phis,
157+
barriers.blocks);
158+
}
159+
172160
DataFlow::Classification
173161
DataFlow::classifyInstruction(SILInstruction *instruction) {
174162
if (instruction == context.definition) {
@@ -193,26 +181,18 @@ bool DataFlow::classificationIsBarrier(Classification classification) {
193181
llvm_unreachable("exhaustive switch not exhaustive?!");
194182
}
195183

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-
}
184+
ArrayRef<SILInstruction *> DataFlow::gens() { return uses.ends; }
208185

209-
bool DataFlow::checkReachableBarrier(SILInstruction *instruction) {
186+
Optional<DataFlow::Effect> DataFlow::effectForInstruction(SILInstruction *instruction) {
187+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
188+
return {Effect::Gen};
210189
auto classification = classifyInstruction(instruction);
211-
visitedInstruction(instruction, classification);
212-
return classificationIsBarrier(classification);
190+
return classificationIsBarrier(classification)
191+
? Optional<Effect>{Effect::Kill}
192+
: llvm::None;
213193
}
214194

215-
bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
195+
Optional<DataFlow::Effect> DataFlow::effectForPhi(SILBasicBlock *block) {
216196
assert(llvm::all_of(block->getArguments(),
217197
[&](auto argument) { return PhiValue(argument); }));
218198

@@ -221,10 +201,7 @@ bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
221201
return classificationIsBarrier(
222202
classifyInstruction(predecessor->getTerminator()));
223203
});
224-
if (isBarrier) {
225-
result.phiBarriers.push_back(block);
226-
}
227-
return isBarrier;
204+
return isBarrier ? Optional<Effect>{Effect::Kill} : llvm::None;
228205
}
229206

230207
/// Hoist the destroy_values of %value.
@@ -256,7 +233,7 @@ bool Rewriter::run() {
256233
//
257234
// A block is a phi barrier iff any of its predecessors' terminators get
258235
// classified as barriers.
259-
for (auto *block : barriers.phiBarriers) {
236+
for (auto *block : barriers.phis) {
260237
madeChange |= createDestroyValue(&block->front());
261238
}
262239

@@ -271,13 +248,9 @@ bool Rewriter::run() {
271248
// have returned true for P, so none of its instructions would ever have been
272249
// classified (except for via checkReachablePhiBarrier, which doesn't record
273250
// terminator barriers).
274-
for (auto instruction : barriers.barriers) {
251+
for (auto instruction : barriers.instructions) {
275252
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
276253
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()));
281254
for (auto *successor : successors) {
282255
madeChange |= createDestroyValue(&successor->front());
283256
}
@@ -301,12 +274,8 @@ bool Rewriter::run() {
301274
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
302275
//
303276
// 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-
}
277+
for (auto *block : barriers.blocks) {
278+
madeChange |= createDestroyValue(&block->front());
310279
}
311280

312281
if (madeChange) {
@@ -324,7 +293,7 @@ bool Rewriter::run() {
324293

325294
bool Rewriter::createDestroyValue(SILInstruction *insertionPoint) {
326295
if (auto *ebi = dyn_cast<DestroyValueInst>(insertionPoint)) {
327-
if (uses.ends.contains(insertionPoint)) {
296+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
328297
reusedDestroyValueInsts.insert(insertionPoint);
329298
return false;
330299
}

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)