Skip to content

Commit 84e23cc

Browse files
authored
Merge pull request #27430 from gottesmm/pr-502b85e746b20821f0d6d3d0f28a16f56fc4f853
[semantic-arc-opts] Change to always use the worklist.
2 parents 32bc769 + 67ff2ae commit 84e23cc

File tree

5 files changed

+188
-105
lines changed

5 files changed

+188
-105
lines changed

lib/Demangling/Demangler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2339,7 +2339,7 @@ std::string Demangler::demangleBridgedMethodParams() {
23392339

23402340
while (!nextIf('_')) {
23412341
auto c = nextChar();
2342-
if (c != 'n' && c != 'b')
2342+
if (c != 'n' && c != 'b' && c != 'g')
23432343
return std::string();
23442344
Str.push_back(c);
23452345
}

lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 107 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "swift/Basic/STLExtras.h"
1616
#include "swift/SIL/BasicBlockUtils.h"
1717
#include "swift/SIL/BranchPropagatedUser.h"
18+
#include "swift/SIL/DebugUtils.h"
1819
#include "swift/SIL/MemAccessUtils.h"
1920
#include "swift/SIL/OwnershipUtils.h"
2021
#include "swift/SIL/SILArgument.h"
@@ -148,9 +149,12 @@ struct SemanticARCOptVisitor
148149
/// Our main worklist. We use this after an initial run through.
149150
SmallBlotSetVector<SILValue, 32> worklist;
150151

151-
/// A secondary work list that we use to store dead trivial instructions to
152-
/// delete after we are done processing the worklist.
153-
SmallBlotSetVector<SILInstruction *, 32> deadTrivialInsts;
152+
/// A set of values that we have visited since the last mutation. We use this
153+
/// to ensure that we do not visit values twice without mutating.
154+
///
155+
/// This is specifically to ensure that we do not go into an infinite loop
156+
/// when visiting phi nodes.
157+
SmallBlotSetVector<SILValue, 16> visitedSinceLastMutation;
154158

155159
SILFunction &F;
156160
Optional<DeadEndBlocks> TheDeadEndBlocks;
@@ -167,8 +171,13 @@ struct SemanticARCOptVisitor
167171
/// the worklist, and then call eraseInstruction on i.
168172
void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i, SILValue newValue) {
169173
worklist.insert(newValue);
174+
for (auto *use : i->getUses()) {
175+
for (SILValue result : use->getUser()->getResults()) {
176+
worklist.insert(result);
177+
}
178+
}
170179
i->replaceAllUsesWith(newValue);
171-
eraseInstruction(i);
180+
eraseInstructionAndAddOperandsToWorklist(i);
172181
}
173182

174183
/// Add all operands of i to the worklist and then call eraseInstruction on
@@ -189,16 +198,86 @@ struct SemanticARCOptVisitor
189198
// the instruction.
190199
for (SILValue result : i->getResults()) {
191200
worklist.erase(result);
201+
visitedSinceLastMutation.erase(result);
192202
}
193-
deadTrivialInsts.erase(i);
194203
i->eraseFromParent();
204+
205+
// Add everything else from visitedSinceLastMutation to the worklist.
206+
for (auto opt : visitedSinceLastMutation) {
207+
if (!opt.hasValue()) {
208+
continue;
209+
}
210+
worklist.insert(*opt);
211+
}
212+
visitedSinceLastMutation.clear();
195213
}
196214

197215
/// The default visitor.
198-
bool visitSILInstruction(SILInstruction *i) { return false; }
216+
bool visitSILInstruction(SILInstruction *i) {
217+
assert(!isGuaranteedForwardingInst(i) &&
218+
"Should have forwarding visitor for all ownership forwarding "
219+
"instructions");
220+
return false;
221+
}
222+
199223
bool visitCopyValueInst(CopyValueInst *cvi);
200224
bool visitBeginBorrowInst(BeginBorrowInst *bbi);
201225
bool visitLoadInst(LoadInst *li);
226+
static bool shouldVisitInst(SILInstruction *i) {
227+
switch (i->getKind()) {
228+
default:
229+
return false;
230+
case SILInstructionKind::CopyValueInst:
231+
case SILInstructionKind::BeginBorrowInst:
232+
case SILInstructionKind::LoadInst:
233+
return true;
234+
}
235+
}
236+
237+
#define FORWARDING_INST(NAME) \
238+
bool visit##NAME##Inst(NAME##Inst *cls) { \
239+
for (SILValue v : cls->getResults()) { \
240+
worklist.insert(v); \
241+
} \
242+
return false; \
243+
}
244+
FORWARDING_INST(Tuple)
245+
FORWARDING_INST(Struct)
246+
FORWARDING_INST(Enum)
247+
FORWARDING_INST(OpenExistentialRef)
248+
FORWARDING_INST(Upcast)
249+
FORWARDING_INST(UncheckedRefCast)
250+
FORWARDING_INST(ConvertFunction)
251+
FORWARDING_INST(RefToBridgeObject)
252+
FORWARDING_INST(BridgeObjectToRef)
253+
FORWARDING_INST(UnconditionalCheckedCast)
254+
FORWARDING_INST(UncheckedEnumData)
255+
FORWARDING_INST(MarkUninitialized)
256+
FORWARDING_INST(SelectEnum)
257+
FORWARDING_INST(DestructureStruct)
258+
FORWARDING_INST(DestructureTuple)
259+
FORWARDING_INST(TupleExtract)
260+
FORWARDING_INST(StructExtract)
261+
FORWARDING_INST(OpenExistentialValue)
262+
FORWARDING_INST(OpenExistentialBoxValue)
263+
FORWARDING_INST(MarkDependence)
264+
#undef FORWARDING_INST
265+
266+
#define FORWARDING_TERM(NAME) \
267+
bool visit##NAME##Inst(NAME##Inst *cls) { \
268+
for (auto succValues : cls->getSuccessorBlockArguments()) { \
269+
for (SILValue v : succValues) { \
270+
worklist.insert(v); \
271+
} \
272+
} \
273+
return false; \
274+
}
275+
276+
FORWARDING_TERM(SwitchEnum)
277+
FORWARDING_TERM(CheckedCastBranch)
278+
FORWARDING_TERM(Branch)
279+
FORWARDING_TERM(CondBranch)
280+
#undef FORWARDING_TERM
202281

203282
bool isWrittenTo(LoadInst *li);
204283

@@ -225,6 +304,14 @@ bool SemanticARCOptVisitor::processWorklist() {
225304
if (!next)
226305
continue;
227306

307+
// First check if this is a value that we have visited since the last time
308+
// we erased an instruction. If we have visited it, skip it. Every time we
309+
// modify something, we should be deleting an instruction, so we have not
310+
// found any further information.
311+
if (!visitedSinceLastMutation.insert(next).second) {
312+
continue;
313+
}
314+
228315
// First check if this is an instruction that is trivially dead. This can
229316
// occur if we eliminate rr traffic resulting in dead projections and the
230317
// like.
@@ -234,7 +321,8 @@ bool SemanticARCOptVisitor::processWorklist() {
234321
// the instruction).
235322
if (auto *defInst = next->getDefiningInstruction()) {
236323
if (isInstructionTriviallyDead(defInst)) {
237-
deadTrivialInsts.insert(defInst);
324+
deleteAllDebugUses(defInst);
325+
eraseInstruction(defInst);
238326
continue;
239327
}
240328
}
@@ -247,23 +335,6 @@ bool SemanticARCOptVisitor::processWorklist() {
247335
}
248336
}
249337

250-
// Then eliminate the rest of the dead trivial insts.
251-
//
252-
// NOTE: We do not need to touch the worklist here since it is guaranteed to
253-
// be empty due to the loop above. We enforce this programatically with the
254-
// assert.
255-
assert(worklist.empty() && "Expected drained worklist so we don't have to "
256-
"remove dead insts form it");
257-
while (!deadTrivialInsts.empty()) {
258-
auto val = deadTrivialInsts.pop_back_val();
259-
if (!val)
260-
continue;
261-
recursivelyDeleteTriviallyDeadInstructions(
262-
*val, true /*force*/,
263-
[&](SILInstruction *i) { deadTrivialInsts.erase(i); });
264-
madeChange = true;
265-
}
266-
267338
return madeChange;
268339
}
269340

@@ -795,59 +866,24 @@ struct SemanticARCOpts : SILFunctionTransform {
795866
"Can not perform semantic arc optimization unless ownership "
796867
"verification is enabled");
797868

798-
// Iterate over all of the arguments, performing small peephole ARC
799-
// optimizations. We assume that the visitor will add any instructions we
800-
// need to recursively to the visitor's worklist. Also, note that we assume
801-
// that we do not look through /any/ sil block arguments here since our
802-
// iteration here is only valid up to SSA.
803-
bool madeChange = false;
804-
805869
SemanticARCOptVisitor visitor(f);
806-
807-
for (auto &bb : f) {
808-
auto ii = bb.rend();
809-
auto start = bb.rbegin();
810-
811-
// If the bb is empty, continue.
812-
if (start == ii)
813-
continue;
814870

815-
// Go to the first instruction to process.
816-
--ii;
817-
818-
// Then until we process the first instruction of the block...
819-
while (ii != start) {
820-
// Move the iterator before ii.
821-
auto tmp = std::next(ii);
822-
823-
// Then try to optimize. If we succeeded, then we deleted
824-
// ii. Move ii from the next value back onto the instruction
825-
// after ii's old value in the block instruction list and then
826-
// process that.
827-
if (visitor.visit(&*ii)) {
828-
madeChange = true;
829-
ii = std::prev(tmp);
830-
continue;
871+
// Add all the results of all instructions that we want to visit to the
872+
// worklist.
873+
for (auto &block : f) {
874+
for (auto &inst : block) {
875+
if (SemanticARCOptVisitor::shouldVisitInst(&inst)) {
876+
for (SILValue v : inst.getResults()) {
877+
visitor.worklist.insert(v);
878+
}
831879
}
832-
833-
// Otherwise, we didn't delete ii. Just visit the next instruction.
834-
--ii;
835880
}
836-
837-
// Finally visit the first instruction of the block.
838-
madeChange |= visitor.visit(&*ii);
839881
}
840882

841-
// Finally drain the worklist on the visitor and process until we reach the
842-
// fixpoint and thus do not have any further work to do.
843-
//
844-
// NOTE: At this point madeChange has already been set to true if we have
845-
// anything in the worklist, so technically we do not need to do this. But I
846-
// would rather represent this state to future proof the pass to be
847-
// "visually" correct.
848-
madeChange |= visitor.processWorklist();
849-
850-
if (madeChange) {
883+
// Then process the worklist. We only destroy instructions, so invalidate
884+
// that. Once we modify the ownership of block arguments, we will need to
885+
// perhaps invalidate branches as well.
886+
if (visitor.processWorklist()) {
851887
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
852888
}
853889
}

0 commit comments

Comments
 (0)