Skip to content

Commit 6100a1b

Browse files
committed
Allow partial consumption of self in deinit.
Basic support for partial consumption in deinit without full support for all future uses of drop_deinit.
1 parent abf23de commit 6100a1b

13 files changed

+400
-78
lines changed

include/swift/SIL/AddressWalker.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030

3131
namespace swift {
3232

33+
enum class TransitiveAddressWalkerTransitiveUseVisitation : uint8_t {
34+
OnlyUses,
35+
OnlyUser,
36+
BothUserAndUses,
37+
};
38+
3339
/// A state structure for findTransitiveUsesForAddress. Intended to be only used
3440
/// a single time. Please always use a new one for every call to
3541
/// findTransitiveUsesForAddress.
@@ -61,11 +67,16 @@ class TransitiveAddressWalker {
6167
/// understand. These cause us to return AddressUseKind::Unknown.
6268
void onError(Operand *use) {}
6369

70+
using TransitiveUseVisitation =
71+
TransitiveAddressWalkerTransitiveUseVisitation;
72+
6473
/// Customization point that causes the walker to treat a specific transitive
6574
/// use as an end point use.
6675
///
6776
/// Example: Visiting a mutable or immutable open_existential_addr.
68-
bool visitTransitiveUseAsEndPointUse(Operand *use) { return false; }
77+
TransitiveUseVisitation visitTransitiveUseAsEndPointUse(Operand *use) {
78+
return TransitiveUseVisitation::OnlyUses;
79+
}
6980

7081
void meet(AddressUseKind other) {
7182
assert(!didInvalidate);
@@ -112,8 +123,12 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
112123
return callVisitUse(use);
113124
}
114125

115-
if (asImpl().visitTransitiveUseAsEndPointUse(use))
116-
return callVisitUse(use);
126+
auto visitation = asImpl().visitTransitiveUseAsEndPointUse(use);
127+
if (visitation != TransitiveUseVisitation::OnlyUses)
128+
callVisitUse(use);
129+
130+
if (visitation == TransitiveUseVisitation::OnlyUser)
131+
return;
117132

118133
for (auto *use : svi->getUses())
119134
addToWorklist(use);

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/Basic/Debug.h"
2525
#include "swift/Basic/FrozenMultiMap.h"
2626
#include "swift/Basic/STLExtras.h"
27+
#include "swift/SIL/ApplySite.h"
2728
#include "swift/SIL/BasicBlockDatastructures.h"
2829
#include "swift/SIL/SILFunction.h"
2930
#include "swift/SIL/SILInstruction.h"
@@ -254,9 +255,7 @@ struct TypeSubElementCount {
254255
TypeSubElementCount(SILType type, SILFunction *fn)
255256
: TypeSubElementCount(type, fn->getModule(), TypeExpansionContext(*fn)) {}
256257

257-
TypeSubElementCount(SILValue value)
258-
: TypeSubElementCount(value->getType(), *value->getModule(),
259-
TypeExpansionContext(*value->getFunction())) {}
258+
TypeSubElementCount(SILValue value);
260259

261260
operator unsigned() const { return number; }
262261

@@ -304,6 +303,39 @@ struct TypeTreeLeafTypeRange {
304303
*startEltOffset + TypeSubElementCount(projectedValue)}};
305304
}
306305

306+
/// Which bits of \p rootValue are involved in \p op.
307+
///
308+
/// This is a subset of (usually equal to) the bits of op->getType() in \p
309+
/// rootValue.
310+
static std::optional<TypeTreeLeafTypeRange> get(Operand *op,
311+
SILValue rootValue) {
312+
auto projectedValue = op->get();
313+
auto startEltOffset = SubElementOffset::compute(projectedValue, rootValue);
314+
if (!startEltOffset)
315+
return std::nullopt;
316+
317+
// A drop_deinit only consumes the deinit bit of its operand.
318+
auto *ddi = dyn_cast<DropDeinitInst>(op->getUser());
319+
if (ddi) {
320+
auto upperBound = *startEltOffset + TypeSubElementCount(projectedValue);
321+
return {{upperBound - 1, upperBound}};
322+
}
323+
324+
// Uses that borrow a value do not involve the deinit bit.
325+
//
326+
// FIXME: This shouldn't be limited to applies.
327+
unsigned deinitBitOffset = 0;
328+
if (op->get()->getType().isValueTypeWithDeinit() &&
329+
op->getOperandOwnership() == OperandOwnership::Borrow &&
330+
ApplySite::isa(op->getUser())) {
331+
deinitBitOffset = 1;
332+
}
333+
334+
return {{*startEltOffset, *startEltOffset +
335+
TypeSubElementCount(projectedValue) -
336+
deinitBitOffset}};
337+
}
338+
307339
/// Given a type \p rootType and a set of needed elements specified by the bit
308340
/// vector \p neededElements, place into \p foundContiguousTypeRanges a set of
309341
/// TypeTreeLeafTypeRanges that are associated with the bit vectors

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ TypeSubElementCount::TypeSubElementCount(SILType type, SILModule &mod,
104104
// our default value, so we can just return.
105105
}
106106

107+
TypeSubElementCount::TypeSubElementCount(SILValue value) : number(1) {
108+
auto whole = TypeSubElementCount(value->getType(), *value->getModule(),
109+
TypeExpansionContext(*value->getFunction()));
110+
// The value produced by a drop_deinit has one fewer subelement than that of
111+
// its type--the deinit bit is not included.
112+
if (isa<DropDeinitInst>(value)) {
113+
assert(value->getType().isValueTypeWithDeinit());
114+
whole = whole - 1;
115+
}
116+
number = whole;
117+
}
118+
107119
//===----------------------------------------------------------------------===//
108120
// MARK: SubElementNumber
109121
//===----------------------------------------------------------------------===//
@@ -201,7 +213,14 @@ SubElementOffset::computeForAddress(SILValue projectionDerivedFromRoot,
201213
projectionDerivedFromRoot = initData->getOperand();
202214
continue;
203215
}
204-
216+
217+
// A drop_deinit consumes the "self" bit at the end of its type. The offset
218+
// is still to the beginning.
219+
if (auto dd = dyn_cast<DropDeinitInst>(projectionDerivedFromRoot)) {
220+
projectionDerivedFromRoot = dd->getOperand();
221+
continue;
222+
}
223+
205224
// Look through wrappers.
206225
if (auto c2m = dyn_cast<CopyableToMoveOnlyWrapperAddrInst>(projectionDerivedFromRoot)) {
207226
projectionDerivedFromRoot = c2m->getOperand();

lib/SILGen/SILGenDestructor.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) {
264264
dd->getDeclContext()->getSelfNominalTypeDecl(),
265265
cleanupLoc);
266266

267+
if (getASTContext().LangOpts.hasFeature(
268+
Feature::MoveOnlyPartialConsumption)) {
269+
if (auto *ddi = dyn_cast<DropDeinitInst>(selfValue)) {
270+
if (auto *mu =
271+
dyn_cast<MarkUnresolvedNonCopyableValueInst>(ddi->getOperand())) {
272+
if (auto *asi = dyn_cast<AllocStackInst>(mu->getOperand())) {
273+
B.createDeallocStack(loc, asi);
274+
}
275+
}
276+
}
277+
}
278+
267279
// Return.
268280
B.createReturn(loc, emitEmptyTuple(loc));
269281
}
@@ -506,15 +518,19 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
506518
void SILGenFunction::emitMoveOnlyMemberDestruction(SILValue selfValue,
507519
NominalTypeDecl *nom,
508520
CleanupLocation cleanupLoc) {
509-
// drop_deinit invalidates any user-defined struct/enum deinit
510-
// before the individual members are destroyed.
511-
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
521+
if (!isa<DropDeinitInst>(selfValue)) {
522+
// drop_deinit invalidates any user-defined struct/enum deinit
523+
// before the individual members are destroyed.
524+
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
525+
}
512526
if (selfValue->getType().isObject()) {
513527
// A destroy value that uses the result of a drop_deinit implicitly performs
514528
// memberwise destruction.
515529
B.emitDestroyValueOperation(cleanupLoc, selfValue);
516530
return;
517531
}
532+
// self has been stored into a temporary
533+
assert(!selfValue->getType().isObject());
518534
if (auto *structDecl = dyn_cast<StructDecl>(nom)) {
519535
for (VarDecl *vd : nom->getStoredProperties()) {
520536
const TypeLowering &ti = getTypeLowering(vd->getTypeInContext());

lib/SILGen/SILGenLValue.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3509,13 +3509,26 @@ RValue SILGenFunction::emitRValueForNonMemberVarDecl(SILLocation loc,
35093509
SILValue accessAddr = UnenforcedFormalAccess::enter(*this, loc, destAddr,
35103510
SILAccessKind::Read);
35113511

3512+
auto isEffectivelyMarkUnresolvedInst = [](auto *inst) -> bool {
3513+
if (!inst)
3514+
return false;
3515+
if (isa<MarkUnresolvedNonCopyableValueInst>(inst))
3516+
return true;
3517+
auto *ddi = dyn_cast<DropDeinitInst>(inst);
3518+
if (!ddi)
3519+
return false;
3520+
return isa<MarkUnresolvedNonCopyableValueInst>(ddi->getOperand());
3521+
};
3522+
35123523
if (accessAddr->getType().isMoveOnly() &&
3513-
!isa<MarkUnresolvedNonCopyableValueInst>(accessAddr)) {
3524+
!isEffectivelyMarkUnresolvedInst(
3525+
accessAddr->getDefiningInstruction())) {
3526+
auto kind =
3527+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign;
35143528
// When loading an rvalue, we should never need to modify the place
35153529
// we're loading from.
3516-
accessAddr = B.createMarkUnresolvedNonCopyableValueInst(
3517-
loc, accessAddr,
3518-
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
3530+
accessAddr =
3531+
B.createMarkUnresolvedNonCopyableValueInst(loc, accessAddr, kind);
35193532
}
35203533

35213534
auto propagateRValuePastAccess = [&](RValue &&rvalue) {

lib/SILGen/SILGenProlog.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,28 +47,48 @@ SILValue SILGenFunction::emitSelfDeclForDestructor(VarDecl *selfDecl) {
4747
selfType = F.mapTypeIntoContext(selfType);
4848
SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);
4949

50+
uint16_t ArgNo = 1; // Hardcoded for destructors.
51+
auto dv = SILDebugVariable(selfDecl->isLet(), ArgNo);
52+
5053
// If we have a move only type, then mark it with
5154
// mark_unresolved_non_copyable_value so we can't escape it.
52-
if (selfType.isMoveOnly()) {
53-
// For now, we do not handle move only class deinits. This is because we
54-
// need to do a bit more refactoring to handle the weird way that it deals
55-
// with ownership. But for simple move only deinits (like struct/enum), that
56-
// are owned, lets mark them as needing to be no implicit copy checked so
57-
// they cannot escape.
58-
if (selfValue->getOwnershipKind() == OwnershipKind::Owned) {
59-
selfValue = B.createMarkUnresolvedNonCopyableValueInst(
60-
selfDecl, selfValue,
55+
//
56+
// For now, we do not handle move only class deinits. This is because we need
57+
// to do a bit more refactoring to handle the weird way that it deals with
58+
// ownership. But for simple move only deinits (like struct/enum), that are
59+
// owned, lets mark them as needing to be no implicit copy checked so they
60+
// cannot escape.
61+
if (selfType.isMoveOnly() && !selfType.isAnyClassReferenceType()) {
62+
if (getASTContext().LangOpts.hasFeature(
63+
Feature::MoveOnlyPartialConsumption)) {
64+
SILValue addr = B.createAllocStack(selfDecl, selfValue->getType(), dv);
65+
addr = B.createMarkUnresolvedNonCopyableValueInst(
66+
selfDecl, addr,
6167
MarkUnresolvedNonCopyableValueInst::CheckKind::
6268
ConsumableAndAssignable);
69+
if (selfValue->getType().isObject()) {
70+
B.createStore(selfDecl, selfValue, addr, StoreOwnershipQualifier::Init);
71+
} else {
72+
B.createCopyAddr(selfDecl, selfValue, addr, IsTake, IsInitialization);
73+
}
74+
// drop_deinit invalidates any user-defined struct/enum deinit
75+
// before the individual members are destroyed.
76+
addr = B.createDropDeinit(selfDecl, addr);
77+
selfValue = addr;
78+
} else {
79+
if (selfValue->getOwnershipKind() == OwnershipKind::Owned) {
80+
selfValue = B.createMarkUnresolvedNonCopyableValueInst(
81+
selfDecl, selfValue,
82+
MarkUnresolvedNonCopyableValueInst::CheckKind::
83+
ConsumableAndAssignable);
84+
}
6385
}
6486
}
6587

6688
VarLocs[selfDecl] = VarLoc::get(selfValue);
6789
SILLocation PrologueLoc(selfDecl);
6890
PrologueLoc.markAsPrologue();
69-
uint16_t ArgNo = 1; // Hardcoded for destructors.
70-
B.createDebugValue(PrologueLoc, selfValue,
71-
SILDebugVariable(selfDecl->isLet(), ArgNo));
91+
B.createDebugValue(PrologueLoc, selfValue, dv);
7292
return selfValue;
7393
}
7494

lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,10 @@ SourceAccess AccessEnforcementSelection::getSourceAccess(SILValue address) {
711711
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableAddrInst>(address))
712712
return getSourceAccess(m->getOperand());
713713

714+
// Recurse through drop_deinit.
715+
if (auto *ddi = dyn_cast<DropDeinitInst>(address))
716+
return getSourceAccess(ddi->getOperand());
717+
714718
// Recurse through moveonlywrapper_to_copyable_box.
715719
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableBoxInst>(address))
716720
return getAccessKindForBox(m->getOperand());

0 commit comments

Comments
 (0)