Skip to content

Commit e202432

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) (cherry picked from commit dc974e8)
1 parent 38786ba commit e202432

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
@@ -1041,7 +1041,12 @@ SILInstruction *SILCombiner::visitIndexAddrInst(IndexAddrInst *IA) {
10411041
/// operation for \p value is required. This differs from a simple `isTrivial`
10421042
/// check, because it treats a value_to_bridge_object instruction as "trivial".
10431043
/// It can also handle non-trivial enums with trivial cases.
1044-
static bool isTrivial(SILValue value, SILFunction *function) {
1044+
///
1045+
/// TODO: Define this as a top-level SILValue API. It needs to be updated
1046+
/// whenever we refine the specification of isTrivial. For example, in the
1047+
/// future we will check for the existence of a user-defined deinitializer and
1048+
/// handle C++ types specially.
1049+
static bool isValueTrivial(SILValue value, SILFunction *function) {
10451050
SmallVector<ValueBase *, 32> workList;
10461051
SmallPtrSet<ValueBase *, 16> visited;
10471052
workList.push_back(value);
@@ -1051,6 +1056,11 @@ static bool isTrivial(SILValue value, SILFunction *function) {
10511056
continue;
10521057
if (isa<ValueToBridgeObjectInst>(v))
10531058
continue;
1059+
1060+
// MoveOnly types may have a user-defined deinit.
1061+
if (v->getType().isPureMoveOnly())
1062+
return false;
1063+
10541064
if (isa<StructInst>(v) || isa<TupleInst>(v)) {
10551065
for (SILValue op : cast<SingleValueInstruction>(v)->getOperandValues()) {
10561066
if (visited.insert(op).second)
@@ -1102,7 +1112,7 @@ SILInstruction *SILCombiner::visitReleaseValueInst(ReleaseValueInst *RVI) {
11021112
RVI->getAtomicity());
11031113

11041114
// ReleaseValueInst of a trivial type is a no-op.
1105-
if (isTrivial(Operand, RVI->getFunction()))
1115+
if (isValueTrivial(Operand, RVI->getFunction()))
11061116
return eraseInstFromFunction(*RVI);
11071117

11081118
// Do nothing for non-trivial non-reference types.
@@ -2523,4 +2533,4 @@ SILCombiner::visitCopyAddrInst(CopyAddrInst *CAI) {
25232533
return Builder.createCopyAddr(
25242534
CAI->getLoc(), Src->getOperand(), Dst->getOperand(),
25252535
CAI->isTakeOfSrc(), CAI->isInitializationOfDest());
2526-
}
2536+
}
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)