Skip to content

Commit 5dd2cf7

Browse files
authored
Merge pull request swiftlang#68110 from atrick/fix-silcombine-moveonly
Fix SILCombine miscompile for discard on non-copyable types
2 parents 3687ddf + a946922 commit 5dd2cf7

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,18 @@ struct AllocStackAnalyzer : SILInstructionVisitor<AllocStackAnalyzer> {
371371
// anything other than the init_existential_addr/open_existential_addr
372372
// container.
373373

374+
// There is no interesting scenario where a non-copyable type should have
375+
// its allocation eliminated. A destroy_addr cannot be removed because it
376+
// may run the struct-deinit, and the lifetime cannot be shortened. A
377+
// copy_addr [take] [init] cannot be replaced by a destroy_addr because the
378+
// destination may hold a 'discard'ed value, which is never destroyed. This
379+
// analysis assumes memory is deinitialized on all paths, which is not the
380+
// case for discarded values. Eventually copyable types may also be
381+
// discarded; to support that, we will leave a drop_deinit_addr in place.
382+
if (ASI->getType().isPureMoveOnly()) {
383+
LegalUsers = false;
384+
return;
385+
}
374386
for (auto *Op : getNonDebugUses(ASI)) {
375387
visit(Op->getUser());
376388

test/SILOptimizer/sil_combine_moveonly.sil

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ enum MaybeFileDescriptor: ~Copyable {
2424
deinit
2525
}
2626

27+
struct Wrapper<T>: ~Copyable {
28+
var t: T
29+
}
30+
31+
sil @getWrappedValue : $@convention(thin) <T> (@in_guaranteed Wrapper<T>) -> @out T
32+
2733
// Test that a release_value is not removed for a struct-with-deinit.
2834
// Doing so would forget the deinit.
2935
//
@@ -71,3 +77,20 @@ bb0:
7177
%9 = tuple ()
7278
return %9 : $()
7379
}
80+
81+
// Test that a move into a discarded value does not result in a destroy_addr
82+
//
83+
// CHECK-LABEL: sil @testNoDeinit : $@convention(method) <T> (@in Wrapper<T>) -> @out T {
84+
// CHECK-NOT: destroy_addr
85+
// CHECK-LABEL: } // end sil function 'testNoDeinit'
86+
sil @testNoDeinit : $@convention(method) <T> (@in Wrapper<T>) -> @out T {
87+
bb0(%0 : $*T, %1 : $*Wrapper<T>):
88+
%2 = function_ref @getWrappedValue : $@convention(thin) <T> (@in_guaranteed Wrapper<T>) -> @out T
89+
%3 = apply %2<T>(%0, %1) : $@convention(thin) <τ_0_0> (@in_guaranteed Wrapper<τ_0_0>) -> @out τ_0_0
90+
%4 = alloc_stack $Wrapper<T>
91+
copy_addr [take] %1 to [init] %4 : $*Wrapper<T>
92+
// discard
93+
dealloc_stack %4 : $*Wrapper<T>
94+
%9 = tuple ()
95+
return %9 : $()
96+
}

0 commit comments

Comments
 (0)