|
16 | 16 |
|
17 | 17 | #define DEBUG_TYPE "silgen-cleanup"
|
18 | 18 |
|
| 19 | +#include "swift/Basic/Defer.h" |
19 | 20 | #include "swift/SIL/BasicBlockUtils.h"
|
20 | 21 | #include "swift/SIL/SILInstruction.h"
|
21 | 22 | #include "swift/SILOptimizer/PassManager/Transforms.h"
|
@@ -48,16 +49,54 @@ struct SILGenCanonicalize final : CanonicalizeInstruction {
|
48 | 49 |
|
49 | 50 | void notifyHasNewUsers(SILValue) override { changed = true; }
|
50 | 51 |
|
51 |
| - SILBasicBlock::iterator deleteDeadOperands(SILBasicBlock::iterator nextII) { |
52 |
| - // Delete trivially dead instructions in non-determistic order. |
| 52 | + /// Delete trivially dead instructions in non-determistic order. |
| 53 | + /// |
| 54 | + /// We either have that nextII is endII or if nextII is not endII then endII |
| 55 | + /// is nextII->getParent()->end(). |
| 56 | + SILBasicBlock::iterator deleteDeadOperands(SILBasicBlock::iterator nextII, |
| 57 | + 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; |
| 63 | + |
53 | 64 | while (!deadOperands.empty()) {
|
54 | 65 | SILInstruction *deadOperInst = *deadOperands.begin();
|
| 66 | + |
55 | 67 | // Make sure at least the first instruction is removed from the set.
|
56 | 68 | deadOperands.erase(deadOperInst);
|
| 69 | + |
| 70 | + // Then add our initial instruction to the will be deleted set. |
| 71 | + willBeDeletedInIteration.insert(deadOperInst); |
| 72 | + SWIFT_DEFER { willBeDeletedInIteration.clear(); }; |
| 73 | + |
57 | 74 | eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) {
|
58 | 75 | LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst);
|
59 |
| - if (nextII == deadInst->getIterator()) |
| 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)) |
60 | 97 | ++nextII;
|
| 98 | + |
| 99 | + // Then remove the instruction from the set. |
61 | 100 | deadOperands.erase(deadInst);
|
62 | 101 | });
|
63 | 102 | }
|
@@ -97,7 +136,7 @@ void SILGenCleanup::run() {
|
97 | 136 | for (auto &bb : function) {
|
98 | 137 | for (auto ii = bb.begin(), ie = bb.end(); ii != ie;) {
|
99 | 138 | ii = sgCanonicalize.canonicalize(&*ii);
|
100 |
| - ii = sgCanonicalize.deleteDeadOperands(ii); |
| 139 | + ii = sgCanonicalize.deleteDeadOperands(ii, ie); |
101 | 140 | }
|
102 | 141 | }
|
103 | 142 | if (sgCanonicalize.changed) {
|
|
0 commit comments