Skip to content

Commit dc974e8

Browse files
committed
Fix SILCombine to preserve a release of a move-only type
The release needs to be preserved in case a user-defined deinit is present in the released type. Checking for move-only is slightly conservative. Fixes rdar://109846094 ([move-only] SILCombine eliminates struct deinitialization)
1 parent 77a6690 commit dc974e8

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

lib/SIL/IR/TypeLowering.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,8 @@ namespace {
23112311
return handleReference(classType, properties);
23122312
}
23132313

2314+
// WARNING: when the specification of trivial types changes, also update
2315+
// the isValueTrivial() API used by SILCombine.
23142316
TypeLowering *visitAnyStructType(CanType structType,
23152317
AbstractionPattern origType,
23162318
StructDecl *D,
@@ -2379,7 +2381,9 @@ namespace {
23792381
return handleAggregateByProperties<LoadableStructTypeLowering>(structType,
23802382
properties);
23812383
}
2382-
2384+
2385+
// WARNING: when the specification of trivial types changes, also update
2386+
// the isValueTrivial() API used by SILCombine.
23832387
TypeLowering *visitAnyEnumType(CanType enumType,
23842388
AbstractionPattern origType,
23852389
EnumDecl *D,

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,12 @@ SILInstruction *SILCombiner::visitIndexAddrInst(IndexAddrInst *IA) {
815815
/// operation for \p value is required. This differs from a simple `isTrivial`
816816
/// check, because it treats a value_to_bridge_object instruction as "trivial".
817817
/// It can also handle non-trivial enums with trivial cases.
818-
static bool isTrivial(SILValue value, SILFunction *function) {
818+
///
819+
/// TODO: Define this as a top-level SILValue API. It needs to be updated
820+
/// whenever we refine the specification of isTrivial. For example, in the
821+
/// future we will check for the existence of a user-defined deinitializer and
822+
/// handle C++ types specially.
823+
static bool isValueTrivial(SILValue value, SILFunction *function) {
819824
SmallVector<ValueBase *, 32> workList;
820825
SmallPtrSet<ValueBase *, 16> visited;
821826
workList.push_back(value);
@@ -825,6 +830,11 @@ static bool isTrivial(SILValue value, SILFunction *function) {
825830
continue;
826831
if (isa<ValueToBridgeObjectInst>(v))
827832
continue;
833+
834+
// MoveOnly types may have a user-defined deinit.
835+
if (v->getType().isPureMoveOnly())
836+
return false;
837+
828838
if (isa<StructInst>(v) || isa<TupleInst>(v)) {
829839
for (SILValue op : cast<SingleValueInstruction>(v)->getOperandValues()) {
830840
if (visited.insert(op).second)
@@ -876,7 +886,7 @@ SILInstruction *SILCombiner::visitReleaseValueInst(ReleaseValueInst *RVI) {
876886
RVI->getAtomicity());
877887

878888
// ReleaseValueInst of a trivial type is a no-op.
879-
if (isTrivial(Operand, RVI->getFunction()))
889+
if (isValueTrivial(Operand, RVI->getFunction()))
880890
return eraseInstFromFunction(*RVI);
881891

882892
// Do nothing for non-trivial non-reference types.
@@ -2297,4 +2307,4 @@ SILCombiner::visitCopyAddrInst(CopyAddrInst *CAI) {
22972307
return Builder.createCopyAddr(
22982308
CAI->getLoc(), Src->getOperand(), Dst->getOperand(),
22992309
CAI->isTakeOfSrc(), CAI->isInitializationOfDest());
2300-
}
2310+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all -sil-combine %s | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
7+
struct S : ~Copyable {
8+
deinit {}
9+
}
10+
11+
// Test that a release_value is not removed for a struct-with-deinit.
12+
// Doing so would forget the deinit.
13+
//
14+
// public func testStructDeinit() {
15+
// var s = S()
16+
// }
17+
//
18+
// CHECK-LABEL: sil @testStructDeinit : $@convention(thin) () -> () {
19+
// CHECK: release_value %{{.*}} : $S
20+
// CHECK-LABEL: } // end sil function 'testStructDeinit'
21+
sil @testStructDeinit : $@convention(thin) () -> () {
22+
bb0:
23+
%0 = struct $S ()
24+
release_value %0 : $S
25+
%5 = tuple ()
26+
return %5 : $()
27+
}
28+

0 commit comments

Comments
 (0)