Skip to content

Add the drop_deinit instruction #65060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,10 @@ final public class MoveValueInst : SingleValueInstruction, UnaryInstruction {
public var fromValue: Value { operand.value }
}

final public class DropDeinitInst : SingleValueInstruction, UnaryInstruction {
public var fromValue: Value { operand.value }
}

final public class StrongCopyUnownedValueInst : SingleValueInstruction, UnaryInstruction {}

final public class StrongCopyUnmanagedValueInst : SingleValueInstruction, UnaryInstruction {}
Expand Down
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/SIL/Registration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public func registerSILClasses() {
register(ProjectBoxInst.self)
register(CopyValueInst.self)
register(MoveValueInst.self)
register(DropDeinitInst.self)
register(EndCOWMutationInst.self)
register(ClassifyBridgeObjectInst.self)
register(PartialApplyInst.self)
Expand Down
23 changes: 23 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6117,6 +6117,29 @@ a type `T` into the move only value space.
The ``lexical`` attribute specifies that the value corresponds to a local
variable in the Swift source.


drop_deinit
```````````

::

sil-instruction ::= 'drop_deinit' sil-operand

%1 = drop_deinit %0 : $T
// T must be a move-only type
// %1 is an @owned T
%3 = drop_deinit %2 : $*T
Copy link
Contributor

@atrick atrick Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eeckstein do we need to support an address form for drop_deinit?
I really hope we don't need that! It's easy to misuse and hard to verify.
In the meantime, can you remove it from the docs?

If we need an address form, we should discuss whether that should be drop_deinit_addr. Generally, instructions should have fixed number and type of operands except in special cases. debug_value is an exception where it works out ok. But an instruction that produces a new address is not straightforward.

// T must be a move-only type
// %2 has type *T

This instruction is a marker for a following destroy instruction to suppress
the call of the move-only type's deinitializer.
The instruction accepts an object or address type.
If its argument is an object type it takes in an `@owned T` and produces a new
`@owned T`. If its argument is an address type, it's an identity projection.

The instruction is only valid in ownership SIL.

release_value
`````````````

Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/MemAccessUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,7 @@ inline bool isAccessStorageIdentityCast(SingleValueInstruction *svi) {
// Simply pass-thru the incoming address.
case SILInstructionKind::MarkUninitializedInst:
case SILInstructionKind::MarkMustCheckInst:
case SILInstructionKind::DropDeinitInst:
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
case SILInstructionKind::MarkDependenceInst:
case SILInstructionKind::CopyValueInst:
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,14 @@ class SILBuilder {
operand, isLexical));
}

DropDeinitInst *createDropDeinit(SILLocation loc, SILValue operand) {
assert(getFunction().hasOwnership());
assert(!operand->getType().isTrivial(getFunction()) &&
"Should not be passing trivial values to this api.");
return insert(new (getModule()) DropDeinitInst(getSILDebugLocation(loc),
operand));
}

MarkUnresolvedMoveAddrInst *createMarkUnresolvedMoveAddr(SILLocation loc,
SILValue srcAddr,
SILValue takeAddr) {
Expand Down
11 changes: 11 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1888,6 +1888,17 @@ void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
recordClonedInstruction(Inst, MVI);
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitDropDeinitInst(DropDeinitInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
if (!getBuilder().hasOwnership()) {
return recordFoldedValue(Inst, getOpValue(Inst->getOperand()));
}
auto *MVI = getBuilder().createDropDeinit(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand()));
recordClonedInstruction(Inst, MVI);
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitMarkMustCheckInst(MarkMustCheckInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -8268,6 +8268,15 @@ class MoveValueInst
void removeIsLexical() { lexical = false; }
};

class DropDeinitInst
: public UnaryInstructionBase<SILInstructionKind::DropDeinitInst,
SingleValueInstruction> {
friend class SILBuilder;

DropDeinitInst(SILDebugLocation DebugLoc, SILValue operand)
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
};

/// Equivalent to a copy_addr to [init] except that it is used for diagnostics
/// and should not be pattern matched. During the diagnostic passes, the "move
/// function" checker for addresses always converts this to a copy_addr [init]
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
// effects relative to other OSSA values like copy_value.
SINGLE_VALUE_INST(MoveValueInst, move_value, SingleValueInstruction, None,
DoesNotRelease)
SINGLE_VALUE_INST(DropDeinitInst, drop_deinit, SingleValueInstruction, None,
DoesNotRelease)
// A canary value inserted by a SIL generating frontend to signal to the move
// checker to check a specific value. Valid only in Raw SIL. The relevant
// checkers should remove the mark_must_check instruction after successfully
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,10 @@ class IRGenSILFunction :
auto e = getLoweredExplosion(i->getOperand());
setLoweredExplosion(i, e);
}
void visitDropDeinitInst(DropDeinitInst *i) {
auto e = getLoweredExplosion(i->getOperand());
setLoweredExplosion(i, e);
}
void visitMarkMustCheckInst(MarkMustCheckInst *i) {
llvm_unreachable("Invalid in Lowered SIL");
}
Expand Down
6 changes: 6 additions & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ OperandOwnershipClassifier::visitStoreBorrowInst(StoreBorrowInst *i) {
return OperandOwnership::TrivialUse;
}

OperandOwnership
OperandOwnershipClassifier::visitDropDeinitInst(DropDeinitInst *i) {
return i->getType().isAddress() ? OperandOwnership::TrivialUse
: OperandOwnership::ForwardingConsume;
}

// Get the OperandOwnership for instantaneous apply, yield, and return uses.
// This does not apply to uses that begin an explicit borrow scope in the
// caller, such as begin_apply.
Expand Down
2 changes: 1 addition & 1 deletion lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2385,7 +2385,7 @@ struct DeallocatorConventions : Conventions {

ParameterConvention
getIndirectSelfParameter(const AbstractionPattern &type) const override {
llvm_unreachable("Deallocators do not have indirect self parameters");
return ParameterConvention::Indirect_In;
}

static bool classof(const Conventions *C) {
Expand Down
4 changes: 4 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1996,6 +1996,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
*this << getIDAndType(I->getOperand());
}

void visitDropDeinitInst(DropDeinitInst *I) {
*this << getIDAndType(I->getOperand());
}

void visitMarkMustCheckInst(MarkMustCheckInst *I) {
using CheckKind = MarkMustCheckInst::CheckKind;
switch (I->getCheckKind()) {
Expand Down
4 changes: 4 additions & 0 deletions lib/SIL/IR/ValueOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ ValueOwnershipKind ValueOwnershipKindClassifier::visitLoadInst(LoadInst *LI) {
llvm_unreachable("Unhandled LoadOwnershipQualifier in switch.");
}

ValueOwnershipKind ValueOwnershipKindClassifier::visitDropDeinitInst(DropDeinitInst *ddi) {
return ddi->getType().isAddress() ? OwnershipKind::None : OwnershipKind::Owned;
}

ValueOwnershipKind
ValueOwnershipKindClassifier::visitPartialApplyInst(PartialApplyInst *PA) {
// partial_apply instructions are modeled as creating an owned value during
Expand Down
9 changes: 9 additions & 0 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3698,6 +3698,15 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
break;
}

case SILInstructionKind::DropDeinitInst: {
if (parseTypedValueRef(Val, B))
return true;
if (parseSILDebugLocation(InstLoc, B))
return true;
ResultVal = B.createDropDeinit(InstLoc, Val);
break;
}

case SILInstructionKind::MarkMustCheckInst: {
StringRef AttrName;
if (!parseSILOptional(AttrName, *this)) {
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Utils/InstructionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
case SILInstructionKind::ValueToBridgeObjectInst:
case SILInstructionKind::MarkDependenceInst:
case SILInstructionKind::MoveValueInst:
case SILInstructionKind::DropDeinitInst:
case SILInstructionKind::MarkMustCheckInst:
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Utils/MemAccessUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,7 @@ AccessPathDefUseTraversal::visitSingleValueUser(SingleValueInstruction *svi,
return IgnoredUse;
}

case SILInstructionKind::DropDeinitInst:
case SILInstructionKind::MarkMustCheckInst: {
// Mark must check goes on the project_box, so it isn't a ref.
assert(!dfs.isRef());
Expand Down
9 changes: 9 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ struct ImmutableAddressUseVerifier {
case SILInstructionKind::IndexAddrInst:
case SILInstructionKind::TailAddrInst:
case SILInstructionKind::IndexRawPointerInst:
case SILInstructionKind::MarkMustCheckInst:
// Add these to our worklist.
for (auto result : inst->getResults()) {
llvm::copy(result->getUses(), std::back_inserter(worklist));
Expand Down Expand Up @@ -5915,6 +5916,14 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"Result and operand must have the same type, today.");
}

void checkDropDeinitInst(DropDeinitInst *ddi) {
require(ddi->getType() == ddi->getOperand()->getType(),
"Result and operand must have the same type.");
require(ddi->getType().isMoveOnlyNominalType(),
"drop_deinit only allowed for move-only types");
require(F.hasOwnership(), "drop_deinit only allowed in OSSA");
}

void checkMarkMustCheckInst(MarkMustCheckInst *i) {
require(i->getModule().getStage() == SILStage::Raw,
"Only valid in Raw SIL! Should have been eliminated by /some/ "
Expand Down
1 change: 1 addition & 0 deletions lib/SILGen/SILGenDestructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ void SILGenFunction::emitMoveOnlyMemberDestruction(SILValue selfValue,
NominalTypeDecl *nom,
CleanupLocation cleanupLoc,
SILBasicBlock *finishBB) {
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
if (selfValue->getType().isAddress()) {
if (auto *structDecl = dyn_cast<StructDecl>(nom)) {
for (VarDecl *vd : nom->getStoredProperties()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,

SILValue SILGenFunction::emitSelfDeclForDestructor(VarDecl *selfDecl) {
// Emit the implicit 'self' argument.
SILType selfType = getLoweredLoadableType(selfDecl->getType());
SILType selfType = getLoweredType(selfDecl->getType());
SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);

// If we have a move only type, then mark it with mark_must_check so we can't
Expand Down
6 changes: 4 additions & 2 deletions lib/SILOptimizer/Mandatory/MoveOnlyDeinitInsertion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ static bool performTransform(SILFunction &fn) {

if (auto *dvi = dyn_cast<DestroyValueInst>(inst)) {
auto destroyType = dvi->getOperand()->getType();
if (destroyType.isMoveOnlyNominalType()) {
if (destroyType.isMoveOnlyNominalType() &&
!isa<DropDeinitInst>(lookThroughOwnershipInsts(dvi->getOperand()))) {
LLVM_DEBUG(llvm::dbgs() << "Handling: " << *dvi);
auto *nom = destroyType.getNominalOrBoundGenericNominal();
assert(nom);
Expand All @@ -88,7 +89,8 @@ static bool performTransform(SILFunction &fn) {

if (auto *dai = dyn_cast<DestroyAddrInst>(inst)) {
auto destroyType = dai->getOperand()->getType();
if (destroyType.isLoadable(fn) && destroyType.isMoveOnlyNominalType()) {
if (destroyType.isLoadable(fn) && destroyType.isMoveOnlyNominalType() &&
!isa<DropDeinitInst>(dai->getOperand())) {
LLVM_DEBUG(llvm::dbgs() << "Handling: " << *dai);
auto *nom = destroyType.getNominalOrBoundGenericNominal();
assert(nom);
Expand Down
4 changes: 4 additions & 0 deletions lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ struct OwnershipModelEliminatorVisitor
eraseInstructionAndRAUW(mvi, mvi->getOperand());
return true;
}
bool visitDropDeinitInst(DropDeinitInst *ddi) {
eraseInstructionAndRAUW(ddi, ddi->getOperand());
return true;
}
bool visitBeginBorrowInst(BeginBorrowInst *bbi) {
eraseInstructionAndRAUW(bbi, bbi->getOperand());
return true;
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::ExplicitCopyValueInst:
case SILInstructionKind::MoveValueInst:
case SILInstructionKind::DropDeinitInst:
case SILInstructionKind::MarkMustCheckInst:
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
case SILInstructionKind::BindMemoryInst:
case SILInstructionKind::RebindMemoryInst:
case SILInstructionKind::MoveValueInst:
case SILInstructionKind::DropDeinitInst:
case SILInstructionKind::MarkMustCheckInst:
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:
Expand Down
8 changes: 8 additions & 0 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
break;
}

case SILInstructionKind::DropDeinitInst: {
auto Ty = MF->getType(TyID);
ResultInst = Builder.createDropDeinit(
Loc,
getLocalValue(ValID, getSILType(Ty, (SILValueCategory)TyCategory, Fn)));
break;
}

case SILInstructionKind::MarkUnresolvedReferenceBindingInst: {
using Kind = MarkUnresolvedReferenceBindingInst::Kind;
auto ty = MF->getType(TyID);
Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 757; // expanded macro definitions
const uint16_t SWIFTMODULE_VERSION_MINOR = 758; // drop_deinit instruction

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::ExplicitCopyValueInst:
case SILInstructionKind::MoveValueInst:
case SILInstructionKind::DropDeinitInst:
case SILInstructionKind::MarkUnresolvedReferenceBindingInst:
case SILInstructionKind::MoveOnlyWrapperToCopyableValueInst:
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:
Expand Down
16 changes: 16 additions & 0 deletions test/SIL/Parser/basic.sil
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ class Class2 {
init()
}

@_moveOnly struct MoveOnlyStruct {
@_hasStorage var i: Int
deinit
}


sil @type_ref1 : $(Class1, Int) -> () // CHECK-LABEL: sil @type_ref1 : $@convention(thin) (Class1, Int)

// Instructions
Expand Down Expand Up @@ -1659,6 +1665,16 @@ bb0(%0 : @guaranteed $Builtin.NativeObject):
return undef : $()
}

// CHECK-LABEL: sil [ossa] @test_drop_deinit :
sil [ossa] @test_drop_deinit : $@convention(thin) (@owned MoveOnlyStruct) -> () {
bb0(%0 : @owned $MoveOnlyStruct):
// CHECK: drop_deinit %0 : $MoveOnlyStruct
%1 = drop_deinit %0 : $MoveOnlyStruct
destroy_value %1 : $MoveOnlyStruct
%3 = tuple ()
return %3 : $()
}

sil @test_destructure_struct_tuple : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.Int32), @owned TestArray2) -> @owned (Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage) {
bb0(%0 : $(Builtin.NativeObject, Builtin.Int32), %1 : $TestArray2):
(%2, %3) = destructure_tuple %0 : $(Builtin.NativeObject, Builtin.Int32)
Expand Down
16 changes: 16 additions & 0 deletions test/SIL/Serialization/basic.sil
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ struct Int32 {

struct EmptyStruct {}

@_moveOnly struct MoveOnlyStruct {
@_hasStorage var i: Int32
deinit
}

// CHECK-LABEL: sil @async_test : $@convention(thin) @async
sil @async_test : $@async () -> () {
bb0:
Expand All @@ -44,6 +49,17 @@ bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2)
return %7 : $(Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage)
}

// CHECK-LABEL: sil [ossa] @test_drop_deinit :
// CHECK: %1 = drop_deinit %0 : $MoveOnlyStruct
// CHECK-LABEL: } // end sil function 'test_drop_deinit'
sil [ossa] @test_drop_deinit : $@convention(thin) (@owned MoveOnlyStruct) -> () {
bb0(%0 : @owned $MoveOnlyStruct):
%1 = drop_deinit %0 : $MoveOnlyStruct
destroy_value %1 : $MoveOnlyStruct
%3 = tuple ()
return %3 : $()
}

sil @test_empty_destructure : $@convention(thin) () -> () {
bb0:
%0 = struct $EmptyStruct()
Expand Down
Loading