Skip to content

Commit 5f69bf5

Browse files
committed
[semantic-arc] Ignore type dependent uses when using a worklist to check for writes.
Otherwise, we hit misc crashes. The worklist should have always been filtering these. I wonder why we have never hit this issue before. I added a new API that filters out type dependent uses called ValueBase::getNonTypeDependentUses() to make it easier to perform def->use worklist traversal ignoring these uses. This mirrors the APIs that we have created for filtering type dependent operands when performing use->def worklist traversal. I also noticed that we are not eliminating a load [copy] that we could in the test case. I am going to file a separate bug report for that work. rdar://79781943
1 parent 910d918 commit 5f69bf5

File tree

3 files changed

+111
-4
lines changed

3 files changed

+111
-4
lines changed

include/swift/SIL/SILValue.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class DeadEndBlocks;
4343
class ValueBaseUseIterator;
4444
class ConsumingUseIterator;
4545
class NonConsumingUseIterator;
46+
class NonTypeDependentUseIterator;
4647
class SILValue;
4748

4849
/// An enumeration which contains values for all the concrete ValueBase
@@ -387,6 +388,9 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
387388
using consuming_use_range = iterator_range<consuming_use_iterator>;
388389
using non_consuming_use_iterator = NonConsumingUseIterator;
389390
using non_consuming_use_range = iterator_range<non_consuming_use_iterator>;
391+
using non_typedependent_use_iterator = NonTypeDependentUseIterator;
392+
using non_typedependent_use_range =
393+
iterator_range<non_typedependent_use_iterator>;
390394

391395
inline use_iterator use_begin() const;
392396
inline use_iterator use_end() const;
@@ -397,6 +401,9 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
397401
inline non_consuming_use_iterator non_consuming_use_begin() const;
398402
inline non_consuming_use_iterator non_consuming_use_end() const;
399403

404+
inline non_typedependent_use_iterator non_typedependent_use_begin() const;
405+
inline non_typedependent_use_iterator non_typedependent_use_end() const;
406+
400407
/// Returns a range of all uses, which is useful for iterating over all uses.
401408
/// To ignore debug-info instructions use swift::getNonDebugUses instead
402409
/// (see comment in DebugUtils.h).
@@ -421,6 +428,10 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
421428
/// Returns a range of all non consuming uses
422429
inline non_consuming_use_range getNonConsumingUses() const;
423430

431+
/// Returns a range of uses that are not classified as a type dependent
432+
/// operand of the user.
433+
inline non_typedependent_use_range getNonTypeDependentUses() const;
434+
424435
template <class T>
425436
inline T *getSingleUserOfType() const;
426437

@@ -1071,6 +1082,7 @@ class Operand {
10711082
friend class ValueBaseUseIterator;
10721083
friend class ConsumingUseIterator;
10731084
friend class NonConsumingUseIterator;
1085+
friend class NonTypeDependentUseIterator;
10741086
template <unsigned N> friend class FixedOperandList;
10751087
friend class TrailingOperandsList;
10761088
};
@@ -1197,6 +1209,41 @@ ValueBase::non_consuming_use_end() const {
11971209
return ValueBase::non_consuming_use_iterator(nullptr);
11981210
}
11991211

1212+
class NonTypeDependentUseIterator : public ValueBaseUseIterator {
1213+
public:
1214+
explicit NonTypeDependentUseIterator(Operand *cur)
1215+
: ValueBaseUseIterator(cur) {}
1216+
NonTypeDependentUseIterator &operator++() {
1217+
assert(Cur && "incrementing past end()!");
1218+
assert(!Cur->isTypeDependent());
1219+
while ((Cur = Cur->NextUse)) {
1220+
if (!Cur->isTypeDependent())
1221+
break;
1222+
}
1223+
return *this;
1224+
}
1225+
1226+
NonTypeDependentUseIterator operator++(int unused) {
1227+
NonTypeDependentUseIterator copy = *this;
1228+
++*this;
1229+
return copy;
1230+
}
1231+
};
1232+
1233+
inline ValueBase::non_typedependent_use_iterator
1234+
ValueBase::non_typedependent_use_begin() const {
1235+
auto cur = FirstUse;
1236+
while (cur && cur->isTypeDependent()) {
1237+
cur = cur->NextUse;
1238+
}
1239+
return ValueBase::non_typedependent_use_iterator(cur);
1240+
}
1241+
1242+
inline ValueBase::non_typedependent_use_iterator
1243+
ValueBase::non_typedependent_use_end() const {
1244+
return ValueBase::non_typedependent_use_iterator(nullptr);
1245+
}
1246+
12001247
inline bool ValueBase::hasOneUse() const {
12011248
auto I = use_begin(), E = use_end();
12021249
if (I == E) return false;
@@ -1242,6 +1289,11 @@ ValueBase::getNonConsumingUses() const {
12421289
return {non_consuming_use_begin(), non_consuming_use_end()};
12431290
}
12441291

1292+
inline ValueBase::non_typedependent_use_range
1293+
ValueBase::getNonTypeDependentUses() const {
1294+
return {non_typedependent_use_begin(), non_typedependent_use_end()};
1295+
}
1296+
12451297
inline bool ValueBase::hasTwoUses() const {
12461298
auto iter = use_begin(), end = use_end();
12471299
for (unsigned i = 0; i < 2; ++i) {

lib/SILOptimizer/SemanticARC/Context.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,20 @@ void Context::verify() const {
3838
bool Context::constructCacheValue(
3939
SILValue initialValue,
4040
SmallVectorImpl<Operand *> &wellBehavedWriteAccumulator) {
41-
SmallVector<Operand *, 8> worklist(initialValue->getUses());
41+
SmallVector<Operand *, 8> worklist(initialValue->getNonTypeDependentUses());
4242

4343
while (!worklist.empty()) {
4444
auto *op = worklist.pop_back_val();
45+
assert(!op->isTypeDependent() &&
46+
"Uses that are type dependent should have been filtered before "
47+
"being inserted into the worklist");
4548
SILInstruction *user = op->getUser();
4649

4750
if (Projection::isAddressProjection(user) ||
4851
isa<ProjectBlockStorageInst>(user)) {
4952
for (SILValue r : user->getResults()) {
50-
llvm::copy(r->getUses(), std::back_inserter(worklist));
53+
llvm::copy(r->getNonTypeDependentUses(),
54+
std::back_inserter(worklist));
5155
}
5256
continue;
5357
}
@@ -59,7 +63,8 @@ bool Context::constructCacheValue(
5963
}
6064

6165
// Otherwise, look through it and continue.
62-
llvm::copy(oeai->getUses(), std::back_inserter(worklist));
66+
llvm::copy(oeai->getNonTypeDependentUses(),
67+
std::back_inserter(worklist));
6368
continue;
6469
}
6570

@@ -93,7 +98,8 @@ bool Context::constructCacheValue(
9398
}
9499

95100
// And then add the users to the worklist and continue.
96-
llvm::copy(bai->getUses(), std::back_inserter(worklist));
101+
llvm::copy(bai->getNonTypeDependentUses(),
102+
std::back_inserter(worklist));
97103
continue;
98104
}
99105

test/SILOptimizer/semantic-arc-opts-loadcopy-to-loadborrow.sil

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Builtin
1313
//////////////////
1414

1515
typealias AnyObject = Builtin.AnyObject
16+
protocol Error {}
1617

1718
enum MyNever {}
1819
enum FakeOptional<T> {
@@ -1450,3 +1451,51 @@ bb1:
14501451
bb2:
14511452
unreachable
14521453
}
1454+
1455+
// Make sure that we ignore type dependent operands when walking through the use
1456+
// list to determine if we are writing to a piece of memory.
1457+
protocol HasClassProperty {
1458+
var c: Klass { get set }
1459+
}
1460+
1461+
struct HasClassPropertyWrapper {
1462+
var h: HasClassProperty
1463+
var otherProperty: Builtin.NativeObject
1464+
}
1465+
1466+
sil @use_hasclassproperty : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error)
1467+
1468+
// Make sure we don't crash on this code. We used to crash by not ignoring the
1469+
// type dependent operand usage of %2.
1470+
//
1471+
// TODO: We should be able to convert %5 to a load_borrow since we know that %1
1472+
// and %4 do not alias. When that is done, update the test case appropriately.
1473+
//
1474+
// CHECK-LABEL: sil [ossa] @use_class_property_wrapper : $@convention(thin) (@inout HasClassPropertyWrapper) -> () {
1475+
// CHECK: load [copy]
1476+
// CHECK: } // end sil function 'use_class_property_wrapper'
1477+
sil [ossa] @use_class_property_wrapper : $@convention(thin) (@inout HasClassPropertyWrapper) -> () {
1478+
bb0(%0 : $*HasClassPropertyWrapper):
1479+
%1 = struct_element_addr %0 : $*HasClassPropertyWrapper, #HasClassPropertyWrapper.h
1480+
%2 = open_existential_addr mutable_access %1 : $*HasClassProperty to $*@opened("85AB1D00-DF62-11EB-A413-ACDE48001122") HasClassProperty
1481+
%3 = function_ref @use_hasclassproperty : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error)
1482+
%4 = struct_element_addr %0 : $*HasClassPropertyWrapper, #HasClassPropertyWrapper.otherProperty
1483+
%5 = load [copy] %4 : $*Builtin.NativeObject
1484+
%f2 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
1485+
apply %f2(%5) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
1486+
try_apply %3<@opened("85AB1D00-DF62-11EB-A413-ACDE48001122") HasClassProperty>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error), normal bb1, error bb2
1487+
1488+
bb1(%str : @owned $Builtin.NativeObject):
1489+
destroy_value %str : $Builtin.NativeObject
1490+
destroy_value %5 : $Builtin.NativeObject
1491+
br bb3
1492+
1493+
bb2(%error : @owned $Error):
1494+
destroy_value %error : $Error
1495+
destroy_value %5 : $Builtin.NativeObject
1496+
br bb3
1497+
1498+
bb3:
1499+
%9999 = tuple()
1500+
return %9999 : $()
1501+
}

0 commit comments

Comments
 (0)