Skip to content

Commit 9ffb35c

Browse files
authored
Merge pull request #27094 from gottesmm/pr-399ba0e079dfa48cb9aba436c0a62bd4087c2304
[semantic-arc-opts] Eliminate dead trivial instructions /after/ the m…
2 parents 0673530 + 8e11010 commit 9ffb35c

File tree

3 files changed

+51
-17
lines changed

3 files changed

+51
-17
lines changed

include/swift/Basic/BlotSetVector.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class BlotSetVector {
6464
iterator end() { return vector.end(); }
6565
const_iterator begin() const { return vector.begin(); }
6666
const_iterator end() const { return vector.end(); }
67+
68+
ArrayRef<Optional<ValueT>> getArray() const { return vector; }
69+
6770
llvm::iterator_range<const_iterator> getRange() const {
6871
return {begin(), end()};
6972
}

lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,18 @@ namespace {
145145
/// the worklist before we delete them.
146146
struct SemanticARCOptVisitor
147147
: SILInstructionVisitor<SemanticARCOptVisitor, bool> {
148+
/// Our main worklist. We use this after an initial run through.
148149
SmallBlotSetVector<SILValue, 32> worklist;
150+
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;
154+
149155
SILFunction &F;
150156
Optional<DeadEndBlocks> TheDeadEndBlocks;
151-
157+
152158
explicit SemanticARCOptVisitor(SILFunction &F) : F(F) {}
153-
159+
154160
DeadEndBlocks &getDeadEndBlocks() {
155161
if (!TheDeadEndBlocks)
156162
TheDeadEndBlocks.emplace(&F);
@@ -167,7 +173,7 @@ struct SemanticARCOptVisitor
167173

168174
/// Add all operands of i to the worklist and then call eraseInstruction on
169175
/// i. Assumes that the instruction doesnt have users.
170-
void eraseInstructionAndAddOptsToWorklist(SILInstruction *i) {
176+
void eraseInstructionAndAddOperandsToWorklist(SILInstruction *i) {
171177
// Then copy all operands into the worklist for future processing.
172178
for (SILValue v : i->getOperandValues()) {
173179
worklist.insert(v);
@@ -184,6 +190,7 @@ struct SemanticARCOptVisitor
184190
for (SILValue result : i->getResults()) {
185191
worklist.erase(result);
186192
}
193+
deadTrivialInsts.erase(i);
187194
i->eraseFromParent();
188195
}
189196

@@ -227,18 +234,7 @@ bool SemanticARCOptVisitor::processWorklist() {
227234
// the instruction).
228235
if (auto *defInst = next->getDefiningInstruction()) {
229236
if (isInstructionTriviallyDead(defInst)) {
230-
madeChange = true;
231-
recursivelyDeleteTriviallyDeadInstructions(
232-
defInst, true/*force*/,
233-
[&](SILInstruction *i) {
234-
for (SILValue operand : i->getOperandValues()) {
235-
worklist.insert(operand);
236-
}
237-
for (SILValue result : i->getResults()) {
238-
worklist.erase(result);
239-
}
240-
++NumEliminatedInsts;
241-
});
237+
deadTrivialInsts.insert(defInst);
242238
continue;
243239
}
244240
}
@@ -251,6 +247,23 @@ bool SemanticARCOptVisitor::processWorklist() {
251247
}
252248
}
253249

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+
254267
return madeChange;
255268
}
256269

@@ -504,7 +517,7 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi)
504517
if (auto *op = cvi->getSingleUse()) {
505518
if (auto *dvi = dyn_cast<DestroyValueInst>(op->getUser())) {
506519
eraseInstruction(dvi);
507-
eraseInstructionAndAddOptsToWorklist(cvi);
520+
eraseInstructionAndAddOperandsToWorklist(cvi);
508521
NumEliminatedInsts += 2;
509522
return true;
510523
}
@@ -531,7 +544,7 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi)
531544
eraseInstruction(destroys.pop_back_val());
532545
++NumEliminatedInsts;
533546
}
534-
eraseInstructionAndAddOptsToWorklist(cvi);
547+
eraseInstructionAndAddOperandsToWorklist(cvi);
535548
++NumEliminatedInsts;
536549
return true;
537550
}

test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,3 +838,21 @@ bb0(%0 : @guaranteed $NativeObjectPair):
838838
apply %func(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever
839839
unreachable
840840
}
841+
842+
// Just make sure that we do not crash on this. We should be able to eliminate
843+
// everything here.
844+
//
845+
// CHECK-LABEL: sil [ossa] @copy_value_with_debug_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () {
846+
// CHECK: bb0
847+
// CHECK-NEXT: tuple
848+
// CHECK-NEXT: return
849+
// CHECK-NEXT: } // end sil function 'copy_value_with_debug_user'
850+
sil [ossa] @copy_value_with_debug_user : $@convention(thin) (@guaranteed NativeObjectPair) -> () {
851+
bb0(%0 : @guaranteed $NativeObjectPair):
852+
%1 = struct_extract %0 : $NativeObjectPair, #NativeObjectPair.obj1
853+
%2 = copy_value %1 : $Builtin.NativeObject
854+
debug_value %2 : $Builtin.NativeObject, let, name "myField"
855+
destroy_value %2 : $Builtin.NativeObject
856+
%9999 = tuple()
857+
return %9999 : $()
858+
}

0 commit comments

Comments
 (0)