Skip to content

Commit 99ca08e

Browse files
committed
Check whether epilogue releases cover all non-trivial fields.
When we have all the epilogue releases. Make sure they cover all the non-trivial parts of the base. Otherwise, treat as if we've found no releases for the base. Currently. this is a NFC other than epilogue dumper. I will wire it up with function signature with next commit. This is part of rdar://22380547
1 parent 992d3aa commit 99ca08e

File tree

5 files changed

+194
-2
lines changed

5 files changed

+194
-2
lines changed

include/swift/SIL/Projection.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace swift {
3636

3737
class SILBuilder;
3838
class ProjectionPath;
39+
using ProjectionPathSet = llvm::DenseSet<ProjectionPath>;
3940
using ProjectionPathList = llvm::SmallVector<Optional<ProjectionPath>, 8>;
4041

4142
enum class SubSeqRelation_t : uint8_t {
@@ -568,6 +569,11 @@ class ProjectionPath {
568569
SILModule *Mod,
569570
ProjectionPathList &P);
570571

572+
/// Return true if the given projection paths in \p CPaths does not cover
573+
/// all the fields with non-trivial semantics, false otherwise.
574+
static bool hasUncoveredNonTrivials(SILType B, SILModule *Mod,
575+
ProjectionPathSet &CPaths);
576+
571577
/// Returns true if the two paths have a non-empty symmetric
572578
/// difference.
573579
///
@@ -945,4 +951,25 @@ class ProjectionTree {
945951

946952
} // end swift namespace
947953

954+
namespace llvm {
955+
using swift::ProjectionPath;
956+
/// Allow ProjectionPath to be used in DenseMap.
957+
template <> struct DenseMapInfo<ProjectionPath> {
958+
static inline ProjectionPath getEmptyKey() {
959+
return ProjectionPath(DenseMapInfo<swift::SILType>::getEmptyKey(),
960+
DenseMapInfo<swift::SILType>::getEmptyKey());
961+
}
962+
static inline ProjectionPath getTombstoneKey() {
963+
return ProjectionPath(DenseMapInfo<swift::SILType>::getTombstoneKey(),
964+
DenseMapInfo<swift::SILType>::getTombstoneKey());
965+
}
966+
static inline unsigned getHashValue(const ProjectionPath &Val) {
967+
return hash_value(Val);
968+
}
969+
static bool isEqual(const ProjectionPath &LHS, const ProjectionPath &RHS) {
970+
return LHS == RHS;
971+
}
972+
};
973+
} // namespace llvm
974+
948975
#endif

include/swift/SILOptimizer/Analysis/ARCAnalysis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ class ConsumedArgToEpilogueReleaseMatcher {
128128
/// and any one the released values.
129129
bool isRedundantRelease(ReleaseList Insts, SILValue Base, SILValue Derived);
130130

131+
/// Return true if we have a release instruction for all the reference
132+
/// sematics part of \p Base.
133+
bool releaseAllNonTrivials(ReleaseList Insts, SILValue Base);
134+
131135
public:
132136
/// Finds matching releases in the return block of the function \p F.
133137
ConsumedArgToEpilogueReleaseMatcher(RCIdentityFunctionInfo *RCFI,

lib/SIL/Projection.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,60 @@ ProjectionPath::expandTypeIntoNodeProjectionPaths(SILType B, SILModule *Mod,
748748
} while (!Worklist.empty());
749749
}
750750

751+
bool ProjectionPath::
752+
hasUncoveredNonTrivials(SILType B, SILModule *Mod, ProjectionPathSet &CPaths) {
753+
llvm::SmallVector<ProjectionPath, 4> Worklist, Paths;
754+
// Push an empty projection path to get started.
755+
ProjectionPath P(B);
756+
Worklist.push_back(P);
757+
do {
758+
// Get the next level projections based on current projection's type.
759+
ProjectionPath PP = Worklist.pop_back_val();
760+
761+
// If this path is part of the covered path, then continue.
762+
if (CPaths.find(PP) != CPaths.end())
763+
continue;
764+
765+
// Get the current type to process.
766+
SILType Ty = PP.getMostDerivedType(*Mod);
767+
768+
// Get the first level projection of the current type.
769+
llvm::SmallVector<Projection, 4> Projections;
770+
Projection::getFirstLevelProjections(Ty, *Mod, Projections);
771+
772+
// Reached the end of the projection tree, this field can not be expanded
773+
// anymore.
774+
if (Projections.empty()) {
775+
Paths.push_back(PP);
776+
continue;
777+
}
778+
779+
// There is at least one projection path that leads to a type with
780+
// reference semantics.
781+
if (Ty.getClassOrBoundGenericClass()) {
782+
Paths.push_back(PP);
783+
continue;
784+
}
785+
786+
// Keep expanding the location.
787+
for (auto &P : Projections) {
788+
ProjectionPath X(B);
789+
X.append(PP);
790+
assert(PP.getMostDerivedType(*Mod) == X.getMostDerivedType(*Mod));
791+
X.append(P);
792+
Worklist.push_back(X);
793+
}
794+
// Keep iterating if the worklist is not empty.
795+
} while (!Worklist.empty());
796+
797+
// Check whether any path leads to a non-trivial type.
798+
for (auto &X : Paths) {
799+
if (!X.getMostDerivedType(*Mod).isTrivial(*Mod))
800+
return true;
801+
}
802+
return false;
803+
}
804+
751805
bool
752806
Projection::operator<(const Projection &Other) const {
753807
// If we have a nominal kind...

lib/SILOptimizer/Analysis/ARCAnalysis.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,25 @@ isRedundantRelease(ReleaseList Insts, SILValue Base, SILValue Derived) {
502502
return false;
503503
}
504504

505+
bool
506+
ConsumedArgToEpilogueReleaseMatcher::
507+
releaseAllNonTrivials(ReleaseList Insts, SILValue Base) {
508+
// Reason about whether all parts are released.
509+
SILModule *Mod = &(*Insts.begin())->getModule();
510+
511+
// These are the list of SILValues that are actually released.
512+
ProjectionPathSet Paths;
513+
for (auto &I : Insts) {
514+
auto PP = ProjectionPath::getProjectionPath(Base, I->getOperand(0));
515+
if (!PP)
516+
return false;
517+
Paths.insert(PP.getValue());
518+
}
519+
520+
// Is there an uncovered non-trivial type.
521+
return !ProjectionPath::hasUncoveredNonTrivials(Base->getType(), Mod, Paths);
522+
}
523+
505524
void ConsumedArgToEpilogueReleaseMatcher::findMatchingReleases(
506525
SILBasicBlock *BB) {
507526
// Iterate over the instructions post-order and find releases associated with
@@ -573,6 +592,17 @@ void ConsumedArgToEpilogueReleaseMatcher::findMatchingReleases(
573592
// Record it.
574593
Iter->second.push_back(Target);
575594
}
595+
596+
// If we can not find a releases for all parts with reference semantics
597+
// that means we did not find all releases for the base.
598+
llvm::DenseSet<SILArgument *> ArgToRemove;
599+
for (auto &Arg : ArgInstMap) {
600+
if (!releaseAllNonTrivials(Arg.second, Arg.first))
601+
ArgToRemove.insert(Arg.first);
602+
}
603+
604+
for (auto &X : ArgToRemove)
605+
ArgInstMap.erase(ArgInstMap.find(X));
576606
}
577607

578608
//===----------------------------------------------------------------------===//

test/SILOptimizer/epilogue_release_dumper.sil

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ struct baz {
2727
init()
2828
}
2929

30+
struct boo {
31+
var tbaz = baz()
32+
var a = 0
33+
init()
34+
}
35+
3036
// CHECK-LABEL: START: sil @single_release_foo
3137
// CHECK: [[IN1:%.*]] = argument of bb0 : $foo
3238
// CHECK: strong_release [[IN1]] : $foo
@@ -80,7 +86,7 @@ bb0(%0 : $baz):
8086
//
8187
// CHECK-LABEL: START: sil @exploded_release_with_interfering_uses
8288
// CHECK: [[IN1:%.*]] = argument of bb0 : $baz
83-
// CHECK: strong_release
89+
// CHECK-NOT: strong_release
8490
// CHECK: END: sil @exploded_release_with_interfering_uses
8591
sil @exploded_release_with_interfering_uses : $@convention(thin) (@owned baz) -> () {
8692
bb0(%0 : $baz):
@@ -97,7 +103,6 @@ bb0(%0 : $baz):
97103
//
98104
// CHECK-LABEL: START: sil @exploded_release_with_overlapping_releases
99105
// CHECK: [[IN1:%.*]] = argument of bb0 : $baz
100-
// CHECK: strong_release
101106
// CHECK-NOT: strong_release
102107
// CHECK: END: sil @exploded_release_with_overlapping_releases
103108
sil @exploded_release_with_overlapping_releases : $@convention(thin) (@owned baz) -> () {
@@ -139,3 +144,75 @@ bb0(%0 : $foo, %1 : $bar):
139144
%3 = tuple ()
140145
return %3 : $()
141146
}
147+
148+
// Make sure we stop at the redundant release.
149+
//
150+
// CHECK-LABEL: START: sil @exploded_release_with_redundant_releases
151+
// CHECK: [[IN1:%.*]] = argument of bb0 : $baz
152+
// CHECK: release_value [[IN1]]
153+
// CHECK: END: sil @exploded_release_with_redundant_releases
154+
sil @exploded_release_with_redundant_releases : $@convention(thin) (@owned baz) -> () {
155+
bb0(%0 : $baz):
156+
%1 = struct_extract %0 : $baz, #baz.start
157+
%2 = struct_extract %0 : $baz, #baz.end
158+
strong_release %1 : $foo
159+
strong_release %2 : $foo
160+
release_value %0 : $baz
161+
%5 = tuple ()
162+
return %5 : $()
163+
}
164+
165+
// Make sure we have an uncovered value type.
166+
//
167+
// CHECK-LABEL: START: sil @exploded_release_with_uncovered_value_semantics
168+
// CHECK: [[IN0:%.*]] = argument of bb0 : $boo
169+
// CHECK: release_value
170+
// CHECK: release_value
171+
// CHECK: END: sil @exploded_release_with_uncovered_value_semantics
172+
sil @exploded_release_with_uncovered_value_semantics : $@convention(thin) (@owned boo) -> () {
173+
bb0(%0 : $boo):
174+
%1 = struct_extract %0 : $boo, #boo.tbaz
175+
%2 = struct_extract %0 : $boo, #boo.a
176+
%3 = struct_extract %1 : $baz, #baz.start
177+
%4 = struct_extract %1 : $baz, #baz.end
178+
release_value %3 : $foo
179+
release_value %4 : $foo
180+
%5 = tuple ()
181+
return %5 : $()
182+
}
183+
184+
// Make sure we have an uncovered reference type.
185+
//
186+
// CHECK-LABEL: START: sil @exploded_release_with_uncovered_reference_semantics
187+
// CHECK: [[IN1:%.*]] = argument of bb0 : $boo
188+
// CHECK-NOT: release_value
189+
// CHECK: END: sil @exploded_release_with_uncovered_reference_semantics
190+
sil @exploded_release_with_uncovered_reference_semantics : $@convention(thin) (@owned boo) -> () {
191+
bb0(%0 : $boo):
192+
%1 = struct_extract %0 : $boo, #boo.tbaz
193+
%2 = struct_extract %0 : $boo, #boo.a
194+
%3 = struct_extract %1 : $baz, #baz.start
195+
%4 = struct_extract %1 : $baz, #baz.end
196+
release_value %3 : $foo
197+
%5 = tuple ()
198+
return %5 : $()
199+
}
200+
201+
// Make sure we have an uncovered reference type due to overlapping release.
202+
//
203+
// CHECK-LABEL: START: sil @exploded_release_with_uncovered_reference_semantics_overlapping_release
204+
// CHECK: [[IN1:%.*]] = argument of bb0 : $boo
205+
// CHECK-NOT: release_value
206+
// CHECK: END: sil @exploded_release_with_uncovered_reference_semantics_overlapping_release
207+
sil @exploded_release_with_uncovered_reference_semantics_overlapping_release : $@convention(thin) (@owned boo) -> () {
208+
bb0(%0 : $boo):
209+
%1 = struct_extract %0 : $boo, #boo.tbaz
210+
%2 = struct_extract %0 : $boo, #boo.a
211+
%3 = struct_extract %1 : $baz, #baz.start
212+
%4 = struct_extract %1 : $baz, #baz.end
213+
release_value %4 : $foo
214+
release_value %3 : $foo
215+
release_value %3 : $foo
216+
%5 = tuple ()
217+
return %5 : $()
218+
}

0 commit comments

Comments
 (0)