Skip to content

Commit 4d593f4

Browse files
authored
Merge pull request swiftlang#36092 from gottesmm/pr-ee0a3941f66885b3a8a02d63f8b7a4efa23a63bf
[silgen-cleanup] Ensure that we move nextII past /all/ instructions we are going to delete no matter order of visitation.
2 parents 4a1d82a + d3aee16 commit 4d593f4

File tree

3 files changed

+66
-8
lines changed

3 files changed

+66
-8
lines changed

lib/SILOptimizer/Mandatory/SILGenCleanup.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#define DEBUG_TYPE "silgen-cleanup"
1818

19+
#include "swift/Basic/Defer.h"
1920
#include "swift/SIL/BasicBlockUtils.h"
2021
#include "swift/SIL/SILInstruction.h"
2122
#include "swift/SILOptimizer/PassManager/Transforms.h"
@@ -48,16 +49,54 @@ struct SILGenCanonicalize final : CanonicalizeInstruction {
4849

4950
void notifyHasNewUsers(SILValue) override { changed = true; }
5051

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+
5364
while (!deadOperands.empty()) {
5465
SILInstruction *deadOperInst = *deadOperands.begin();
66+
5567
// Make sure at least the first instruction is removed from the set.
5668
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+
5774
eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) {
5875
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))
6097
++nextII;
98+
99+
// Then remove the instruction from the set.
61100
deadOperands.erase(deadInst);
62101
});
63102
}
@@ -97,7 +136,7 @@ void SILGenCleanup::run() {
97136
for (auto &bb : function) {
98137
for (auto ii = bb.begin(), ie = bb.end(); ii != ie;) {
99138
ii = sgCanonicalize.canonicalize(&*ii);
100-
ii = sgCanonicalize.deleteDeadOperands(ii);
139+
ii = sgCanonicalize.deleteDeadOperands(ii, ie);
101140
}
102141
}
103142
if (sgCanonicalize.changed) {

test/SILOptimizer/silgen_cleanup.sil

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,26 @@ bb0(%0 : $*X3, %1 : @guaranteed $Builtin.NativeObject):
216216
%12 = tuple ()
217217
return %12 : $()
218218
}
219+
220+
// We used to hit a memory error on this test.
221+
//
222+
// CHECK-LABEL: sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () {
223+
// CHECK: bb0(
224+
// CHECK-NEXT: destroy_value
225+
// CHECK-NEXT: tuple
226+
// CHECK-NEXT: return
227+
// CHECK: } // end sil function 'testDestructureTupleNoCrash'
228+
sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () {
229+
bb0(%0 : @owned $(Builtin.NativeObject, Builtin.NativeObject)):
230+
(%1, %2) = destructure_tuple %0 : $(Builtin.NativeObject, Builtin.NativeObject)
231+
debug_value %1 : $Builtin.NativeObject, let, name "key"
232+
debug_value %2 : $Builtin.NativeObject, let, name "value"
233+
%3 = begin_borrow %1 : $Builtin.NativeObject
234+
end_borrow %3 : $Builtin.NativeObject
235+
%4 = begin_borrow %2 : $Builtin.NativeObject
236+
end_borrow %4 : $Builtin.NativeObject
237+
destroy_value %2 : $Builtin.NativeObject
238+
destroy_value %1 : $Builtin.NativeObject
239+
%9999 = tuple()
240+
return %9999 : $()
241+
}

validation-test/stdlib/Dictionary.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
// RUN: %target-codesign %t/Dictionary && %line-directive %t/main.swift -- %target-run %t/Dictionary
88
// REQUIRES: executable_test
99

10-
// rdar71933996
11-
// UNSUPPORTED: swift_test_mode_optimize
12-
// UNSUPPORTED: swift_test_mode_optimize_size
13-
1410
import StdlibUnittest
1511
import StdlibCollectionUnittest
1612

0 commit comments

Comments
 (0)