Skip to content

Commit 45f08bd

Browse files
committed
MemoryLifetime: support init_existential_addr and open_existential_addr
Also, relax the check for enums a bit. Instead of only accepting single-payload enums, just require that the payload type is the same for an enum location.
1 parent b73285d commit 45f08bd

File tree

9 files changed

+117
-69
lines changed

9 files changed

+117
-69
lines changed

include/swift/SIL/MemoryLifetime.h

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,16 @@ class MemoryLocations {
189189
/// small. They can be handled separately with handleSingleBlockLocations().
190190
llvm::SmallVector<SingleValueInstruction *, 16> singleBlockLocations;
191191

192-
/// A Cache for single-payload enums.
193-
llvm::DenseMap<SILType, EnumElementDecl *> singlePayloadEnums;
194-
195192
/// The bit-set of locations for which numNonTrivialFieldsNotCovered is > 0.
196193
Bits nonTrivialLocations;
197194

198-
/// If true, support init_enum_data_addr and unchecked_take_enum_data_addr
199-
bool handleEnumDataProjections;
195+
/// If true, support init_enum_data_addr, unchecked_take_enum_data_addr,
196+
/// init_existential_addr and open_existential_addr.
197+
bool handleNonTrivialProjections;
200198

201199
public:
202-
MemoryLocations(bool handleEnumDataProjections) :
203-
handleEnumDataProjections(handleEnumDataProjections) {}
200+
MemoryLocations(bool handleNonTrivialProjections) :
201+
handleNonTrivialProjections(handleNonTrivialProjections) {}
204202

205203
MemoryLocations(const MemoryLocations &) = delete;
206204
MemoryLocations &operator=(const MemoryLocations &) = delete;
@@ -297,15 +295,6 @@ class MemoryLocations {
297295
// (locationIdx, fieldNr) -> subLocationIdx
298296
using SubLocationMap = llvm::DenseMap<std::pair<unsigned, unsigned>, unsigned>;
299297

300-
/// Returns the payload case of a single-payload enum.
301-
///
302-
/// Returns null if \p enumTy is not a single-payload enum.
303-
/// We are currently only handling enum data projections for single-payload
304-
/// enums, because it's much simpler to represent them with Locations. We
305-
/// could also support multi-payload enums, but that gets complicated. Most
306-
/// importantly, we can handle Swift.Optional.
307-
EnumElementDecl *getSinglePayloadEnumCase(SILType enumTy);
308-
309298
/// Helper function called by analyzeLocation to check all uses of the
310299
/// location recursively.
311300
///

lib/SIL/Verifier/MemoryLifetime.cpp

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ MemoryLocations::Location::Location(SILValue val, unsigned index, int parentIdx)
100100
parentIdx(parentIdx) {
101101
assert(((parentIdx >= 0) ==
102102
(isa<StructElementAddrInst>(val) || isa<TupleElementAddrInst>(val) ||
103-
isa<InitEnumDataAddrInst>(val) || isa<UncheckedTakeEnumDataAddrInst>(val)))
103+
isa<InitEnumDataAddrInst>(val) || isa<UncheckedTakeEnumDataAddrInst>(val) ||
104+
isa<InitExistentialAddrInst>(val) || isa<OpenExistentialAddrInst>(val)))
104105
&& "sub-locations can only be introduced with struct/tuple/enum projections");
105106
setBitAndResize(subLocations, index);
106107
setBitAndResize(selfAndParents, index);
@@ -280,35 +281,17 @@ void MemoryLocations::clear() {
280281
nonTrivialLocations.clear();
281282
}
282283

283-
static EnumElementDecl *computeSinglePayloadEnumCase(SILType enumTy) {
284-
EnumDecl *enumDecl = enumTy.getEnumOrBoundGenericEnum();
285-
if (!enumDecl)
286-
return nullptr;
287-
EnumElementDecl *payloadCase = nullptr;
288-
for (EnumElementDecl *elem : enumDecl->getAllElements()) {
289-
if (elem->hasAssociatedValues()) {
290-
if (payloadCase)
291-
return nullptr;
292-
payloadCase = elem;
293-
}
294-
}
295-
return payloadCase;
296-
}
297-
298-
EnumElementDecl *MemoryLocations::getSinglePayloadEnumCase(SILType enumTy) {
299-
auto iter = singlePayloadEnums.find(enumTy);
300-
if (iter != singlePayloadEnums.end())
301-
return iter->second;
302-
303-
EnumElementDecl *&payloadCase = singlePayloadEnums[enumTy];
304-
payloadCase = computeSinglePayloadEnumCase(enumTy);
305-
return payloadCase;
306-
}
307-
308284
bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx,
309285
SmallVectorImpl<SILValue> &collectedVals,
310286
SubLocationMap &subLocationMap) {
311287
for (Operand *use : V->getUses()) {
288+
// We can safely ignore type dependent operands, because the lifetime of a
289+
// type is decoupled from the lifetime of its value. For example, even if
290+
// the result of an open_existential_addr is destroyed its type is still
291+
// valid.
292+
if (use->isTypeDependent())
293+
continue;
294+
312295
SILInstruction *user = use->getUser();
313296

314297
// We only handle addr-instructions which are planned to be used with
@@ -335,21 +318,18 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx
335318
collectedVals, subLocationMap))
336319
return false;
337320
break;
321+
case SILInstructionKind::InitExistentialAddrInst:
322+
case SILInstructionKind::OpenExistentialAddrInst:
338323
case SILInstructionKind::InitEnumDataAddrInst:
339324
case SILInstructionKind::UncheckedTakeEnumDataAddrInst:
340-
if (!handleEnumDataProjections)
341-
return false;
342-
if (!getSinglePayloadEnumCase(user->getOperand(0)->getType()))
325+
if (!handleNonTrivialProjections)
343326
return false;
344327
// The payload is represented as a single sub-location of the enum.
345328
if (!analyzeAddrProjection(cast<SingleValueInstruction>(user), locIdx,
346329
/*fieldNr*/ 0, collectedVals, subLocationMap))
347330
return false;
348331
break;
349332
case SILInstructionKind::InjectEnumAddrInst:
350-
if (!getSinglePayloadEnumCase(user->getOperand(0)->getType()))
351-
return false;
352-
break;
353333
case SILInstructionKind::LoadInst:
354334
case SILInstructionKind::StoreInst:
355335
case SILInstructionKind::StoreBorrowInst:
@@ -368,6 +348,7 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx
368348
case SILInstructionKind::YieldInst:
369349
case SILInstructionKind::DeallocStackInst:
370350
case SILInstructionKind::SwitchEnumAddrInst:
351+
case SILInstructionKind::WitnessMethodInst:
371352
break;
372353
default:
373354
return false;
@@ -411,6 +392,24 @@ bool MemoryLocations::analyzeAddrProjection(
411392
idx = loc.parentIdx;
412393
} while (idx >= 0);
413394
}
395+
} else if (!isa<OpenExistentialAddrInst>(projection)) {
396+
Location *loc = &locations[subLocIdx];
397+
if (loc->representativeValue->getType() != projection->getType()) {
398+
assert(isa<InitEnumDataAddrInst>(projection) ||
399+
isa<UncheckedTakeEnumDataAddrInst>(projection) ||
400+
isa<InitExistentialAddrInst>(projection));
401+
402+
// We can only handle a single enum payload type for a location or or a
403+
// single concrete existential type. Mismatching types can have a differnt
404+
// number of (non-trivial) sub-locations and we cannot handle this.
405+
// But we ignore opened existential types, because those cannot have
406+
// sub-locations (there cannot be an address projection on an
407+
// open_existential_addr).
408+
if (!isa<OpenExistentialAddrInst>(loc->representativeValue))
409+
return false;
410+
assert(loc->representativeValue->getType().isOpenedExistential());
411+
loc->representativeValue = projection;
412+
}
414413
}
415414

416415
if (!analyzeLocationUsesRecursively(projection, subLocIdx, collectedVals,
@@ -451,11 +450,6 @@ void MemoryLocations::initFieldsCounter(Location &loc) {
451450
}
452451
return;
453452
}
454-
if (EnumElementDecl *elem = getSinglePayloadEnumCase(ty)) {
455-
// The payload is represented as a single sub-location of the enum.
456-
loc.updateFieldCounters(ty.getEnumElementType(elem, function), +1);
457-
return;
458-
}
459453
loc.updateFieldCounters(ty, +1);
460454
}
461455

@@ -695,7 +689,7 @@ class MemoryLifetimeVerifier {
695689

696690
public:
697691
MemoryLifetimeVerifier(SILFunction *function) :
698-
function(function), locations(/*handleEnumDataProjections*/ true) {}
692+
function(function), locations(/*handleNonTrivialProjections*/ true) {}
699693

700694
/// The main entry point to verify the lifetime of all memory locations in
701695
/// the function.
@@ -1152,12 +1146,16 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
11521146
requireNoStoreBorrowLocation(IEAI->getOperand(), &I);
11531147
break;
11541148
}
1149+
case SILInstructionKind::InitExistentialAddrInst:
11551150
case SILInstructionKind::InitEnumDataAddrInst: {
1156-
SILValue enumAddr = cast<InitEnumDataAddrInst>(&I)->getOperand();
1157-
requireBitsClear(bits, enumAddr, &I);
1158-
requireNoStoreBorrowLocation(enumAddr, &I);
1151+
SILValue addr = I.getOperand(0);
1152+
requireBitsClear(bits, addr, &I);
1153+
requireNoStoreBorrowLocation(addr, &I);
11591154
break;
11601155
}
1156+
case SILInstructionKind::OpenExistentialAddrInst:
1157+
requireBitsSet(bits, cast<OpenExistentialAddrInst>(&I)->getOperand(), &I);
1158+
break;
11611159
case SILInstructionKind::UncheckedTakeEnumDataAddrInst: {
11621160
// Note that despite the name, unchecked_take_enum_data_addr does _not_
11631161
// "take" the payload of the Swift.Optional enum. This is a terrible

lib/SILOptimizer/Transforms/DestroyHoisting.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ class DestroyHoisting {
124124
public:
125125
DestroyHoisting(SILFunction *function, DominanceAnalysis *DA) :
126126
function(function),
127-
// We currently don't handle enum data projections, because they cannot
128-
// re-created easily. We could support this in future.
129-
locations(/*handleEnumDataProjections*/ false), DA(DA) {}
127+
// We currently don't handle enum and existential projections, because they
128+
// cannot be re-created easily. We could support this in future.
129+
locations(/*handleNonTrivialProjections*/ false), DA(DA) {}
130130

131131
bool hoistDestroys();
132132
};

test/SIL/memory_lifetime.sil

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,3 +560,35 @@ bb0(%0 : $*U):
560560
%5 = tuple ()
561561
return %5 : $()
562562
}
563+
564+
protocol P {}
565+
566+
sil [ossa] @test_existentials : $@convention(thin) <U: P> (@in_guaranteed U) -> () {
567+
bb0(%0 : $*U):
568+
%s = alloc_stack $P
569+
%i = init_existential_addr %s : $*P, $U
570+
copy_addr %0 to [initialization] %i : $*U
571+
%o = open_existential_addr immutable_access %s : $*P to $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
572+
%s2 = alloc_stack $@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
573+
copy_addr %o to [initialization] %s2 : $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
574+
destroy_addr %s2 : $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
575+
dealloc_stack %s2 : $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
576+
destroy_addr %s : $*P
577+
dealloc_stack %s : $*P
578+
%5 = tuple ()
579+
return %5 : $()
580+
}
581+
582+
sil [ossa] @test_init_two_existentials : $@convention(thin) <U: P, V: P> (@in_guaranteed U, @in_guaranteed V) -> () {
583+
bb0(%0 : $*U, %1 : $*V):
584+
%s = alloc_stack $P
585+
%t = init_existential_addr %s : $*P, $U
586+
copy_addr %0 to [initialization] %t : $*U
587+
destroy_addr %s : $*P
588+
%v = init_existential_addr %s : $*P, $V
589+
copy_addr %1 to [initialization] %v : $*V
590+
destroy_addr %s : $*P
591+
dealloc_stack %s : $*P
592+
%5 = tuple ()
593+
return %5 : $()
594+
}

test/SIL/memory_lifetime_failures.sil

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,3 +445,32 @@ bb0(%0 : $*U):
445445
%5 = tuple ()
446446
return %5 : $()
447447
}
448+
449+
protocol P {}
450+
451+
// CHECK: SIL memory lifetime failure in @test_init_existential: memory is not initialized, but should
452+
sil [ossa] @test_init_existential : $@convention(thin) <U: P> (@in_guaranteed U) -> () {
453+
bb0(%0 : $*U):
454+
%s = alloc_stack $P
455+
%i = init_existential_addr %s : $*P, $U
456+
destroy_addr %s : $*P
457+
dealloc_stack %s : $*P
458+
%5 = tuple ()
459+
return %5 : $()
460+
}
461+
462+
// CHECK: SIL memory lifetime failure in @test_open_existential: memory is not initialized, but should
463+
sil [ossa] @test_open_existential : $@convention(thin) <U: P> (@in_guaranteed U) -> () {
464+
bb0(%0 : $*U):
465+
%s = alloc_stack $P
466+
%o = open_existential_addr immutable_access %s : $*P to $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
467+
%s2 = alloc_stack $@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
468+
copy_addr %o to [initialization] %s2 : $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
469+
destroy_addr %s2 : $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
470+
dealloc_stack %s2 : $*@opened("5B62392E-7C62-11EB-BF90-D0817AD9985D") P
471+
destroy_addr %s : $*P
472+
dealloc_stack %s : $*P
473+
%5 = tuple ()
474+
return %5 : $()
475+
}
476+

test/SILOptimizer/accessed_storage_analysis_ossa.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ enum TreeB<T> {
451451
sil [ossa] @readIndirectEnum : $@convention(thin) <T> (@in TreeB<T>) -> (@out TreeB<T>) {
452452
bb0(%0 : $*TreeB<T>, %1 : $*TreeB<T>):
453453
%enumAddr = unchecked_take_enum_data_addr %1 : $*TreeB<T>, #TreeB.Branch!enumelt
454-
%box = load [copy] %enumAddr : $*<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } <T>
454+
%box = load [take] %enumAddr : $*<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } <T>
455455
%boxAddr = project_box %box : $<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } <T>, 0
456456
%boxAccess = begin_access [read] [dynamic] %boxAddr : $*(left: TreeB<T>, right: TreeB<T>)
457457
%leftAddr = tuple_element_addr %boxAccess : $*(left: TreeB<T>, right: TreeB<T>), 0

test/SILOptimizer/existential_transform_extras_ossa.sil

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ bb0(%0 : $*P):
113113
%6 = open_existential_addr immutable_access %4 : $*P to $*@opened("3CB58EC4-ECED-11E8-9798-D0817AD4059B") P
114114
%7 = witness_method $@opened("3CB58EC4-ECED-11E8-9798-D0817AD4059B") P, #P.foo : <Self where Self : P> (Self) -> () -> Int32, %6 : $*@opened("3CB58EC4-ECED-11E8-9798-D0817AD4059B") P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int32
115115
%8 = apply %7<@opened("3CB58EC4-ECED-11E8-9798-D0817AD4059B") P>(%6) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int32
116+
destroy_addr %4 : $*P
116117
dealloc_stack %4 : $*P
117118
return %8 : $Int32
118119
}
@@ -191,7 +192,9 @@ bb0(%0 : $*P, %1 : $*P):
191192
%19 = tuple_extract %17 : $(Builtin.Int32, Builtin.Int1), 1
192193
cond_fail %19 : $Builtin.Int1
193194
%21 = struct $Int32 (%18 : $Builtin.Int32)
195+
destroy_addr %9 : $*P
194196
dealloc_stack %9 : $*P
197+
destroy_addr %4 : $*P
195198
dealloc_stack %4 : $*P
196199
return %21 : $Int32
197200
}

test/SILOptimizer/looprotate_ossa.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ bb3:
204204
// CHECK-LABEL: sil [ossa] @looprotate_with_opened_archetype :
205205
// CHECK: return
206206
// CHECK-LABEL: } // end sil function 'looprotate_with_opened_archetype'
207-
sil [ossa] @looprotate_with_opened_archetype : $@convention(thin) (Int32, @in P) -> Int32 {
207+
sil [ossa] @looprotate_with_opened_archetype : $@convention(thin) (Int32, @in_guaranteed P) -> Int32 {
208208
bb0(%0 : $Int32, %25: $*P):
209209
%1 = struct_extract %0 : $Int32, #Int32._value
210210
%2 = integer_literal $Builtin.Int32, 0

test/SILOptimizer/sil_combine_apply_ossa.sil

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -403,20 +403,17 @@ bb0(%0 : $*MutatingProto, %1 : $MStruct):
403403
}
404404

405405
// CHECK-LABEL: sil [ossa] @dont_replace_copied_self_in_mutating_method_call2
406-
sil [ossa] @dont_replace_copied_self_in_mutating_method_call2 : $@convention(thin) (@thick MutatingProto.Type) -> (@out MutatingProto) {
407-
bb0(%0 : $*MutatingProto, %1 : $@thick MutatingProto.Type):
408-
%alloc1 = alloc_stack $MutatingProto, let, name "p"
406+
sil [ossa] @dont_replace_copied_self_in_mutating_method_call2 : $@convention(thin) (@thick MutatingProto.Type, @in_guaranteed MutatingProto) -> (@out MutatingProto) {
407+
bb0(%0 : $*MutatingProto, %1 : $@thick MutatingProto.Type, %2 : $*MutatingProto):
409408
%openType = open_existential_metatype %1 : $@thick MutatingProto.Type to $@thick (@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto).Type
410-
%initType = init_existential_addr %alloc1 : $*MutatingProto, $@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto
411409
%alloc2 = alloc_stack $MutatingProto
412-
copy_addr %alloc1 to [initialization] %alloc2 : $*MutatingProto
410+
copy_addr %2 to [initialization] %alloc2 : $*MutatingProto
413411
%oeaddr = open_existential_addr mutable_access %alloc2 : $*MutatingProto to $*@opened("6E02DCF6-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto
414412
%witmethod = witness_method $@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto, #MutatingProto.mutatingMethod : <Self where Self : MutatingProto> (inout Self) -> () -> (), %openType : $@thick (@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto).Type : $@convention(witness_method: MutatingProto) <τ_0_0 where τ_0_0 : MutatingProto> (@inout τ_0_0) -> ()
415413
// CHECK: apply {{%.*}}<@opened("6E02DCF6-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto>({{%.*}}) : $@convention(witness_method: MutatingProto) <τ_0_0 where τ_0_0 : MutatingProto> (@inout τ_0_0) -> () // type-defs
416414
%apply = apply %witmethod<@opened("6E02DCF6-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto>(%oeaddr) : $@convention(witness_method: MutatingProto) <τ_0_0 where τ_0_0 : MutatingProto> (@inout τ_0_0) -> ()
417415
copy_addr [take] %alloc2 to [initialization] %0 : $*MutatingProto
418416
dealloc_stack %alloc2 : $*MutatingProto
419-
dealloc_stack %alloc1 : $*MutatingProto
420417
%27 = tuple ()
421418
return %27 : $()
422419
}
@@ -1108,7 +1105,7 @@ bb2:
11081105
// CHECK-NEXT: apply [[FN]]<@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol>([[OPEN]])
11091106
// CHECK-NEXT: tuple
11101107
// CHECK-NEXT: return
1111-
sil [ossa] @test_existential_partial_apply_apply : $@convention(thin) (@in FakeProtocol) -> () {
1108+
sil [ossa] @test_existential_partial_apply_apply : $@convention(thin) (@in_guaranteed FakeProtocol) -> () {
11121109
bb0(%0: $*FakeProtocol):
11131110
%o = open_existential_addr immutable_access %0 : $*FakeProtocol to $*@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol
11141111
%f1 = witness_method $@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol, #FakeProtocol.requirement, %o : $*@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol : $@convention(witness_method: FakeProtocol) <T where T : FakeProtocol> (@in_guaranteed T) -> ()

0 commit comments

Comments
 (0)