Skip to content

Commit bfdec5c

Browse files
authored
Merge pull request #66357 from atrick/5.9-fix-dropdeinit
[5.9][move-only] Fix SIL representation and SILOptimizer to preserve value deinits.
2 parents 174b204 + 0fa4d98 commit bfdec5c

34 files changed

+610
-289
lines changed

include/swift/SIL/InstructionUtils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ SILValue stripCastsWithoutMarkDependence(SILValue V);
4040
/// begin_borrow instructions.
4141
SILValue lookThroughOwnershipInsts(SILValue v);
4242

43+
/// Reverse of lookThroughOwnershipInsts.
44+
///
45+
/// Return true if \p visitor returned true for all uses.
46+
bool visitNonOwnershipUses(SILValue value,
47+
function_ref<bool(Operand *)> visitor);
48+
4349
/// Return the underlying SILValue after looking through all copy_value
4450
/// instructions.
4551
SILValue lookThroughCopyValueInsts(SILValue v);

include/swift/SIL/SILMoveOnlyDeinit.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ class SILMoveOnlyDeinit final : public SILAllocated<SILMoveOnlyDeinit> {
6161

6262
NominalTypeDecl *getNominalDecl() const { return nominalDecl; }
6363

64-
SILFunction *getImplementation() const { return funcImpl; }
64+
SILFunction *getImplementation() const {
65+
assert(funcImpl);
66+
return funcImpl;
67+
}
6568

6669
IsSerialized_t isSerialized() const {
6770
return serialized ? IsSerialized : IsNotSerialized;

include/swift/SIL/SILNodes.def

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,8 +463,15 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
463463
// effects relative to other OSSA values like copy_value.
464464
SINGLE_VALUE_INST(MoveValueInst, move_value, SingleValueInstruction, None,
465465
DoesNotRelease)
466-
SINGLE_VALUE_INST(DropDeinitInst, drop_deinit, SingleValueInstruction, None,
467-
DoesNotRelease)
466+
// A drop_deinit has no user-level side effects. It does, however, change the
467+
// information about the owenership of its operand, and therefore cannot be
468+
// arbitrarily deleted. It effectively wraps the type of the operand so that
469+
// the resulting value is an equivalent type without a user-defined deinit.
470+
//
471+
// TODO: Add an OwnershipEffect type of side effect for instructions like
472+
// drop_deinit to indicate their ownership effects.
473+
SINGLE_VALUE_INST(DropDeinitInst, drop_deinit, SingleValueInstruction,
474+
MayHaveSideEffects, DoesNotRelease)
468475
// A canary value inserted by a SIL generating frontend to signal to the move
469476
// checker to check a specific value. Valid only in Raw SIL. The relevant
470477
// checkers should remove the mark_must_check instruction after successfully

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ PASS(PartialApplySimplification, "partial-apply-simplification",
462462
"Transform partial_apply instructions into explicit closure box constructions")
463463
PASS(MovedAsyncVarDebugInfoPropagator, "sil-moved-async-var-dbginfo-propagator",
464464
"Propagate debug info from moved async vars after coroutine funclet boundaries")
465-
PASS(MoveOnlyDeinitInsertion, "sil-move-only-deinit-insertion",
465+
PASS(MoveOnlyDeinitDevirtualization, "sil-move-only-deinit-devirtualization",
466466
"After running move only checking, convert last destroy_values to deinit calls")
467467
PASS(MoveOnlyBorrowToDestructureTransform,
468468
"sil-move-only-borrow-to-destructure",

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ void collectUsesOfValue(SILValue V,
141141
/// value itself)
142142
void eraseUsesOfValue(SILValue value);
143143

144+
/// Return true if \p type is a value type (struct/enum) that requires
145+
/// deinitialization beyond destruction of its members.
146+
bool hasValueDeinit(SILType type);
147+
148+
/// Return true if \p value has a value type (struct/enum) that requires
149+
/// deinitialization beyond destruction of its members.
150+
inline bool hasValueDeinit(SILValue value) {
151+
return hasValueDeinit(value->getType());
152+
}
153+
144154
/// Gets the concrete value which is stored in an existential box.
145155
/// Returns %value in following pattern:
146156
///
@@ -381,8 +391,10 @@ ignore_expect_uses(ValueBase *value) {
381391
/// operations from it. These can be simplified and removed.
382392
bool simplifyUsers(SingleValueInstruction *inst);
383393

384-
/// True if a type can be expanded
385-
/// without a significant increase to code size.
394+
/// True if a type can be expanded without a significant increase to code size.
395+
///
396+
/// False if expanding a type is invalid. For example, expanding a
397+
/// struct-with-deinit drops the deinit.
386398
bool shouldExpand(SILModule &module, SILType ty);
387399

388400
/// Check if the value of value is computed by means of a simple initialization.

lib/IRGen/IRGenSIL.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,8 +1249,7 @@ class IRGenSILFunction :
12491249
setLoweredExplosion(i, e);
12501250
}
12511251
void visitDropDeinitInst(DropDeinitInst *i) {
1252-
auto e = getLoweredExplosion(i->getOperand());
1253-
setLoweredExplosion(i, e);
1252+
llvm_unreachable("only valid in ownership SIL");
12541253
}
12551254
void visitMarkMustCheckInst(MarkMustCheckInst *i) {
12561255
llvm_unreachable("Invalid in Lowered SIL");

lib/SIL/IR/TypeLowering.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,19 @@ namespace {
15751575
return B.createStruct(loc, getLoweredType(), values);
15761576
}
15771577

1578+
void
1579+
emitLoweredDestroyValue(SILBuilder &B, SILLocation loc, SILValue aggValue,
1580+
TypeExpansionKind loweringStyle) const override {
1581+
// A value type with a deinit cannot be memberwise destroyed.
1582+
if (auto *nominal = getLoweredType().getNominalOrBoundGenericNominal()) {
1583+
if (nominal->getValueTypeDestructor()) {
1584+
emitDestroyValue(B, loc, aggValue);
1585+
return;
1586+
}
1587+
}
1588+
Super::emitLoweredDestroyValue(B, loc, aggValue, loweringStyle);
1589+
}
1590+
15781591
private:
15791592
void lowerChildren(TypeConverter &TC,
15801593
SmallVectorImpl<Child> &children) const override {

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,30 @@ SILValue swift::lookThroughOwnershipInsts(SILValue v) {
3939
}
4040
}
4141

42+
bool swift::visitNonOwnershipUses(SILValue value,
43+
function_ref<bool(Operand *)> visitor) {
44+
// All ownership insts have a single operand, so a recursive walk is
45+
// sufficient and cannot revisit operands.
46+
for (Operand *use : value->getUses()) {
47+
auto *user = use->getUser();
48+
switch (user->getKind()) {
49+
default:
50+
if (!visitor(use))
51+
return false;
52+
53+
break;
54+
case SILInstructionKind::MoveValueInst:
55+
case SILInstructionKind::CopyValueInst:
56+
case SILInstructionKind::BeginBorrowInst:
57+
if (!visitNonOwnershipUses(cast<SingleValueInstruction>(user), visitor))
58+
return false;
59+
60+
break;
61+
}
62+
}
63+
return true;
64+
}
65+
4266
SILValue swift::lookThroughCopyValueInsts(SILValue val) {
4367
while (auto *cvi =
4468
dyn_cast_or_null<CopyValueInst>(val->getDefiningInstruction())) {

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2698,6 +2698,7 @@ void swift::visitAccessedAddress(SILInstruction *I,
26982698
case SILInstructionKind::DeinitExistentialValueInst:
26992699
case SILInstructionKind::DestroyAddrInst:
27002700
case SILInstructionKind::DestroyValueInst:
2701+
case SILInstructionKind::DropDeinitInst:
27012702
case SILInstructionKind::EndAccessInst:
27022703
case SILInstructionKind::EndApplyInst:
27032704
case SILInstructionKind::EndBorrowInst:

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3547,6 +3547,13 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
35473547
"destructure with none ownership kind operand and non-none "
35483548
"ownership kind result?!");
35493549
}
3550+
if (operandTy.getNominalOrBoundGenericNominal()
3551+
->getValueTypeDestructor()) {
3552+
require(
3553+
isa<DropDeinitInst>(lookThroughOwnershipInsts(DSI->getOperand())),
3554+
"a destructure of a move-only-type-with-deinit requires a "
3555+
"drop_deinit");
3556+
}
35503557
}
35513558
}
35523559

@@ -5968,12 +5975,40 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
59685975
"Result and operand must have the same type, today.");
59695976
}
59705977

5978+
// check that a drop_deinit can only ever be destroyed or destructured
5979+
void checkDropDeinitUses(DropDeinitInst *ddi) {
5980+
// Address-type drop_deinit has no special structural requirements. It just
5981+
// sits there and blocks optimization on the allocation and downstream uses
5982+
// of the address. If we want to optimize around address-type drop_deinit,
5983+
// then we need a seperate verifier for its requirements.
5984+
if (ddi->getType().isAddress())
5985+
return;
5986+
5987+
visitNonOwnershipUses(ddi, [&](Operand *use) {
5988+
auto *user = use->getUser();
5989+
require(isa<DestroyValueInst>(user)
5990+
|| isa<EndLifetimeInst>(user)
5991+
|| isa<DestructureStructInst>(user)
5992+
|| isa<SwitchEnumInst>(user),
5993+
"A drop_deinit can only be destroyed or destructured");
5994+
return true;
5995+
});
5996+
}
5997+
59715998
void checkDropDeinitInst(DropDeinitInst *ddi) {
5972-
require(ddi->getType() == ddi->getOperand()->getType(),
5999+
require(F.hasOwnership(), "drop_deinit only allowed in OSSA");
6000+
6001+
auto type = ddi->getType();
6002+
require(type == ddi->getOperand()->getType(),
59736003
"Result and operand must have the same type.");
5974-
require(ddi->getType().isMoveOnlyNominalType(),
6004+
require(type.isMoveOnlyNominalType(),
59756005
"drop_deinit only allowed for move-only types");
5976-
require(F.hasOwnership(), "drop_deinit only allowed in OSSA");
6006+
require(type.getNominalOrBoundGenericNominal()
6007+
->getValueTypeDestructor(), "drop_deinit only allowed for "
6008+
"struct/enum types that define a deinit");
6009+
assert(!type.isTrivial(F) && "a type with a deinit is nontrivial");
6010+
6011+
checkDropDeinitUses(ddi);
59776012
}
59786013

59796014
void checkMarkMustCheckInst(MarkMustCheckInst *i) {

0 commit comments

Comments
 (0)