Skip to content

Commit 5900a0f

Browse files
authored
Merge pull request #37016 from gottesmm/pr-eb1b4b484d8990f6f5cb6706dba952b3e27cdc59
[sil-optimizer] Make InstructionDeleter and related APIs to use an InstModCallback instead of a notification callback.
2 parents b882a12 + 7b55cbc commit 5900a0f

File tree

8 files changed

+425
-382
lines changed

8 files changed

+425
-382
lines changed

include/swift/SILOptimizer/Utils/DebugOptUtils.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,43 @@ inline void deleteAllDebugUses(SILInstruction *inst,
4343
}
4444
}
4545

46+
/// Erases the instruction \p I from it's parent block and deletes it, including
47+
/// all debug instructions which use \p I.
48+
/// Precondition: The instruction may only have debug instructions as uses.
49+
/// If the iterator \p InstIter references any deleted instruction, it is
50+
/// incremented.
51+
///
52+
/// \p callbacks InstModCallback to use.
53+
///
54+
/// Returns an iterator to the next non-deleted instruction after \p I.
55+
inline SILBasicBlock::iterator
56+
eraseFromParentWithDebugInsts(SILInstruction *inst,
57+
InstModCallbacks callbacks = InstModCallbacks()) {
58+
auto nextII = std::next(inst->getIterator());
59+
60+
auto results = inst->getResults();
61+
bool foundAny;
62+
do {
63+
foundAny = false;
64+
for (auto result : results) {
65+
while (!result->use_empty()) {
66+
foundAny = true;
67+
auto *user = result->use_begin()->getUser();
68+
assert(user->isDebugInstruction());
69+
if (nextII == user->getIterator())
70+
++nextII;
71+
callbacks.deleteInst(user);
72+
}
73+
}
74+
} while (foundAny);
75+
76+
// Just matching what eraseFromParentWithDebugInsts is today.
77+
if (nextII == inst->getIterator())
78+
++nextII;
79+
callbacks.deleteInst(inst, false /*do not notify*/);
80+
return nextII;
81+
}
82+
4683
} // namespace swift
4784

4885
#endif

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 256 additions & 232 deletions
Large diffs are not rendered by default.

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- OSLogOptimizer.cpp - Optimizes calls to OS Log ===//
1+
//===--- OSLogOptimizer.cpp - Optimizes calls to OS Log -------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -68,6 +68,8 @@
6868
/// 'substituteConstants' and 'emitCodeForSymbolicValue'. The remaining
6969
/// functions in the file implement the subtasks and utilities needed by the
7070
/// above functions.
71+
///
72+
//===----------------------------------------------------------------------===//
7173

7274
#include "swift/AST/ASTContext.h"
7375
#include "swift/AST/DiagnosticEngine.h"
@@ -1108,8 +1110,6 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage,
11081110
return errorDetected;
11091111
}
11101112

1111-
using CallbackTy = llvm::function_ref<void(SILInstruction *)>;
1112-
11131113
/// Return true iff the given address-valued instruction has only stores into
11141114
/// it. This function tests for the conditions under which a call, that was
11151115
/// constant evaluated, that writes into the address-valued instruction can be
@@ -1184,8 +1184,7 @@ static bool hasOnlyStoreUses(SingleValueInstruction *addressInst) {
11841184
/// of begin_apply. This will also fix the lifetimes of the deleted instructions
11851185
/// whenever possible.
11861186
static void forceDeleteAllocStack(SingleValueInstruction *inst,
1187-
InstructionDeleter &deleter,
1188-
CallbackTy callback) {
1187+
InstructionDeleter &deleter) {
11891188
SmallVector<SILInstruction *, 8> users;
11901189
for (Operand *use : inst->getUses())
11911190
users.push_back(use->getUser());
@@ -1194,30 +1193,31 @@ static void forceDeleteAllocStack(SingleValueInstruction *inst,
11941193
if (isIncidentalUse(user))
11951194
continue;
11961195
if (isa<DestroyAddrInst>(user)) {
1197-
deleter.forceDelete(user, callback);
1196+
deleter.forceDelete(user);
11981197
continue;
11991198
}
12001199
if (isa<BeginAccessInst>(user)) {
1201-
forceDeleteAllocStack(cast<BeginAccessInst>(user), deleter, callback);
1200+
forceDeleteAllocStack(cast<BeginAccessInst>(user), deleter);
12021201
continue;
12031202
}
1204-
deleter.forceDeleteAndFixLifetimes(user, callback);
1203+
deleter.forceDeleteAndFixLifetimes(user);
12051204
}
1206-
deleter.forceDelete(inst, callback);
1205+
deleter.forceDelete(inst);
12071206
}
12081207

12091208
/// Delete \c inst , if it is dead, along with its dead users and invoke the
12101209
/// callback whever an instruction is deleted.
1211-
static void deleteInstructionWithUsersAndFixLifetimes(
1212-
SILInstruction *inst, InstructionDeleter &deleter, CallbackTy callback) {
1210+
static void
1211+
deleteInstructionWithUsersAndFixLifetimes(SILInstruction *inst,
1212+
InstructionDeleter &deleter) {
12131213
// If this is an alloc_stack, it can be eliminated as long as it is only
12141214
// stored into or destroyed.
12151215
if (AllocStackInst *allocStack = dyn_cast<AllocStackInst>(inst)) {
12161216
if (hasOnlyStoreUses(allocStack))
1217-
forceDeleteAllocStack(allocStack, deleter, callback);
1217+
forceDeleteAllocStack(allocStack, deleter);
12181218
return;
12191219
}
1220-
deleter.recursivelyDeleteUsersIfDead(inst, callback);
1220+
deleter.recursivelyDeleteUsersIfDead(inst);
12211221
}
12221222

12231223
/// Try to dead-code eliminate the OSLogMessage instance \c oslogMessage passed
@@ -1226,31 +1226,35 @@ static void deleteInstructionWithUsersAndFixLifetimes(
12261226
/// \returns true if elimination is successful and false if it is not successful
12271227
/// and diagnostics is emitted.
12281228
static bool tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) {
1229-
InstructionDeleter deleter;
12301229
// List of instructions that are possibly dead.
12311230
SmallVector<SILInstruction *, 4> worklist = {oslogMessage};
12321231
// Set of all deleted instructions.
12331232
SmallPtrSet<SILInstruction *, 4> deletedInstructions;
1233+
1234+
auto callbacks =
1235+
InstModCallbacks().onNotifyWillBeDeleted([&](SILInstruction *deadInst) {
1236+
// Add operands of all deleted instructions to the worklist so that
1237+
// they can be recursively deleted if possible.
1238+
for (Operand &operand : deadInst->getAllOperands()) {
1239+
if (SILInstruction *definingInstruction =
1240+
operand.get()->getDefiningInstruction()) {
1241+
if (!deletedInstructions.count(definingInstruction))
1242+
worklist.push_back(definingInstruction);
1243+
}
1244+
}
1245+
(void)deletedInstructions.insert(deadInst);
1246+
});
1247+
InstructionDeleter deleter(callbacks);
1248+
12341249
unsigned startIndex = 0;
12351250
while (startIndex < worklist.size()) {
12361251
SILInstruction *inst = worklist[startIndex++];
12371252
if (deletedInstructions.count(inst))
12381253
continue;
1239-
deleteInstructionWithUsersAndFixLifetimes(
1240-
inst, deleter, [&](SILInstruction *deadInst) {
1241-
// Add operands of all deleted instructions to the worklist so that
1242-
// they can be recursively deleted if possible.
1243-
for (Operand &operand : deadInst->getAllOperands()) {
1244-
if (SILInstruction *definingInstruction =
1245-
operand.get()->getDefiningInstruction()) {
1246-
if (!deletedInstructions.count(definingInstruction))
1247-
worklist.push_back(definingInstruction);
1248-
}
1249-
}
1250-
(void)deletedInstructions.insert(deadInst);
1251-
});
1254+
deleteInstructionWithUsersAndFixLifetimes(inst, deleter);
12521255
}
12531256
deleter.cleanUpDeadInstructions();
1257+
12541258
// If the OSLogMessage instance is not deleted, either we couldn't see the
12551259
// body of the log call or there is a bug in the library implementation.
12561260
// Assuming that the library implementation is correct, it means that either

lib/SILOptimizer/Mandatory/SILGenCleanup.cpp

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -55,50 +55,27 @@ struct SILGenCanonicalize final : CanonicalizeInstruction {
5555
/// is nextII->getParent()->end().
5656
SILBasicBlock::iterator deleteDeadOperands(SILBasicBlock::iterator nextII,
5757
SILBasicBlock::iterator endII) {
58-
// Each iteration, we store the instructions that will be deleted in the
59-
// iteration here and use it to ensure that nextII is moved past /all/
60-
// instructions that we are going to delete no matter the order (since we
61-
// are visiting instructions in non-deterministic order).
62-
SmallPtrSet<SILInstruction *, 16> willBeDeletedInIteration;
58+
auto callbacks = InstModCallbacks().onDelete([&](SILInstruction *deadInst) {
59+
LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst);
60+
61+
// If nextII is the instruction we are going to delete, move nextII past
62+
// it.
63+
if (deadInst->getIterator() == nextII)
64+
++nextII;
65+
66+
// Then remove the instruction from the set and delete it.
67+
deadOperands.erase(deadInst);
68+
deadInst->eraseFromParent();
69+
});
6370

6471
while (!deadOperands.empty()) {
6572
SILInstruction *deadOperInst = *deadOperands.begin();
6673

6774
// Make sure at least the first instruction is removed from the set.
6875
deadOperands.erase(deadOperInst);
6976

70-
// Then add our initial instruction to the will be deleted set.
71-
willBeDeletedInIteration.insert(deadOperInst);
72-
SWIFT_DEFER { willBeDeletedInIteration.clear(); };
73-
74-
eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) {
75-
LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst);
76-
77-
// Add our instruction to the will be deleted set.
78-
willBeDeletedInIteration.insert(deadInst);
79-
80-
// Then look through /all/ instructions that we are going to delete in
81-
// this iteration until we hit the end of the list. This ensures that in
82-
// a situation like the following:
83-
//
84-
// ```
85-
// (%1, %2) = multi_result_inst %0 (a)
86-
// inst_to_delete %1 (b)
87-
// inst_to_delete %2 (c)
88-
// ```
89-
//
90-
// If nextII is on (b), but we visit (c) before visiting (b), then we
91-
// will end up with nextII on (c) after we are done and then delete
92-
// (c). In contrast by using the set when we process (b) after (c), we
93-
// first see that (b) is nextII [since it is in the set] so move to (c)
94-
// and then see that (c) is in the set as well (since we inserted it
95-
// previously) and skip that.
96-
while (nextII != endII && willBeDeletedInIteration.count(&*nextII))
97-
++nextII;
98-
99-
// Then remove the instruction from the set.
100-
deadOperands.erase(deadInst);
101-
});
77+
// Then delete this instruction/everything else that we can.
78+
eliminateDeadInstruction(deadOperInst, callbacks);
10279
}
10380
return nextII;
10481
}

lib/SILOptimizer/Utils/ConstantFolding.cpp

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,13 @@ ConstantFolder::processWorkList() {
17271727
I->eraseFromParent();
17281728
});
17291729

1730+
auto callbacks =
1731+
InstModCallbacks().onDelete([&](SILInstruction *instToDelete) {
1732+
WorkList.remove(instToDelete);
1733+
InvalidateInstructions = true;
1734+
instToDelete->eraseFromParent();
1735+
});
1736+
17301737
// An out parameter array that we use to return new simplified results from
17311738
// constantFoldInstruction.
17321739
SmallVector<SILValue, 8> ConstantFoldedResults;
@@ -1736,8 +1743,6 @@ ConstantFolder::processWorkList() {
17361743

17371744
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << *I);
17381745

1739-
Callback(I);
1740-
17411746
// Replace assert_configuration instructions by their constant value. We
17421747
// want them to be replace even if we can't fully propagate the constant.
17431748
if (AssertConfiguration != SILOptions::DisableReplacement)
@@ -1751,29 +1756,28 @@ ConstantFolder::processWorkList() {
17511756
// Schedule users for constant folding.
17521757
WorkList.insert(AssertConfInt);
17531758
// Delete the call.
1754-
eliminateDeadInstruction(BI);
1755-
1756-
InvalidateInstructions = true;
1759+
eliminateDeadInstruction(BI, callbacks);
17571760
continue;
17581761
}
17591762

17601763
// Kill calls to conditionallyUnreachable if we've folded assert
17611764
// configuration calls.
17621765
if (isApplyOfBuiltin(*BI, BuiltinValueKind::CondUnreachable)) {
17631766
assert(BI->use_empty() && "use of conditionallyUnreachable?!");
1764-
recursivelyDeleteTriviallyDeadInstructions(BI, /*force*/ true);
1767+
recursivelyDeleteTriviallyDeadInstructions(BI, /*force*/ true,
1768+
callbacks);
17651769
InvalidateInstructions = true;
17661770
continue;
17671771
}
17681772
}
1773+
17691774
// Replace a known availability.version semantic call.
17701775
if (isApplyOfKnownAvailability(*I)) {
17711776
if (auto apply = dyn_cast<ApplyInst>(I)) {
17721777
SILBuilderWithScope B(I);
17731778
auto tru = B.createIntegerLiteral(apply->getLoc(), apply->getType(), 1);
17741779
apply->replaceAllUsesWith(tru);
1775-
eliminateDeadInstruction(
1776-
I, [&](SILInstruction *DeadI) { WorkList.remove(DeadI); });
1780+
eliminateDeadInstruction(I, callbacks);
17771781
WorkList.insert(tru);
17781782
InvalidateInstructions = true;
17791783
}
@@ -1822,9 +1826,7 @@ ConstantFolder::processWorkList() {
18221826
if (constantFoldGlobalStringTablePointerBuiltin(cast<BuiltinInst>(I),
18231827
EnableDiagnostics)) {
18241828
// Here, the bulitin instruction got folded, so clean it up.
1825-
eliminateDeadInstruction(
1826-
I, [&](SILInstruction *DeadI) { WorkList.remove(DeadI); });
1827-
InvalidateInstructions = true;
1829+
eliminateDeadInstruction(I, callbacks);
18281830
}
18291831
continue;
18301832
}
@@ -1839,10 +1841,8 @@ ConstantFolder::processWorkList() {
18391841
auto *cfi = builder.createCondFail(I->getLoc(), I->getOperand(0),
18401842
sli->getValue());
18411843
WorkList.insert(cfi);
1842-
recursivelyDeleteTriviallyDeadInstructions(
1843-
I, /*force*/ true,
1844-
[&](SILInstruction *DeadI) { WorkList.remove(DeadI); });
1845-
InvalidateInstructions = true;
1844+
recursivelyDeleteTriviallyDeadInstructions(I, /*force*/ true,
1845+
callbacks);
18461846
}
18471847
}
18481848
continue;
@@ -1851,10 +1851,8 @@ ConstantFolder::processWorkList() {
18511851
if (isApplyOfBuiltin(*I, BuiltinValueKind::IsConcrete)) {
18521852
if (constantFoldIsConcrete(cast<BuiltinInst>(I))) {
18531853
// Here, the bulitin instruction got folded, so clean it up.
1854-
recursivelyDeleteTriviallyDeadInstructions(
1855-
I, /*force*/ true,
1856-
[&](SILInstruction *DeadI) { WorkList.remove(DeadI); });
1857-
InvalidateInstructions = true;
1854+
recursivelyDeleteTriviallyDeadInstructions(I, /*force*/ true,
1855+
callbacks);
18581856
}
18591857
continue;
18601858
}
@@ -1875,7 +1873,7 @@ ConstantFolder::processWorkList() {
18751873
}
18761874

18771875
// Go through all users of the constant and try to fold them.
1878-
InstructionDeleter deleter;
1876+
InstructionDeleter deleter(callbacks);
18791877
for (auto Result : I->getResults()) {
18801878
for (auto *Use : Result->getUses()) {
18811879
SILInstruction *User = Use->getUser();
@@ -2022,12 +2020,10 @@ ConstantFolder::processWorkList() {
20222020
}
20232021
}
20242022
}
2023+
20252024
// Eagerly DCE. We do this after visiting all users to ensure we don't
20262025
// invalidate the uses iterator.
2027-
deleter.cleanUpDeadInstructions([&](SILInstruction *DeadI) {
2028-
WorkList.remove(DeadI);
2029-
InvalidateInstructions = true;
2030-
});
2026+
deleter.cleanUpDeadInstructions();
20312027
}
20322028

20332029
// TODO: refactor this code outside of the method. Passes should not merge

0 commit comments

Comments
 (0)