Skip to content

Commit

Permalink
[semantic-sil] Add special unmanaged_{retain,release}_value instructi…
Browse files Browse the repository at this point in the history
…ons for unpaired retain/release operations in semantic sil.

The reason why I am introducing special instructions is so I can maintain the
qualified ownership API wedge in between qualified SIL and the rest of the ARC
instructions that are pervasively used in the compiler.

These instructions in the future /could/ be extended to just take @sil_unmanaged
operands directly, but I want to maintain flexibility to take regular
non-trivial operands in the short term.

rdar://29791263
  • Loading branch information
gottesmm committed Jan 19, 2017
1 parent fc3f02a commit 2da18c7
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 6 deletions.
34 changes: 34 additions & 0 deletions docs/SIL.rst
Expand Up @@ -3236,6 +3236,23 @@ For aggregate types, especially enums, it is typically both easier
and more efficient to reason about aggregate copies than it is to
reason about copies of the subobjects.

unmanaged_retain_value
``````````````````````

::

sil-instruction ::= 'unmanaged_retain_value' sil-value

unmanaged_retain_value %0 : $A

This instruction has the same local semantics as ``retain_value`` but:

* Is valid in ownership qualified SIL.
* Is not intended to be statically paired at compile time by the compiler.

The intention is that this instruction is used to implement unmanaged
constructs.

copy_value
``````````

Expand Down Expand Up @@ -3285,6 +3302,23 @@ For aggregate types, especially enums, it is typically both easier
and more efficient to reason about aggregate destroys than it is to
reason about destroys of the subobjects.

unmanaged_release_value
```````````````````````

::

sil-instruction ::= 'unmanaged_release_value' sil-value

unmanaged_release_value %0 : $A

This instruction has the same local semantics as ``release_value`` but:

* Is valid in ownership qualified SIL.
* Is not intended to be statically paired at compile time by the compiler.

The intention is that this instruction is used to implement unmanaged
constructs.

destroy_value
`````````````

Expand Down
14 changes: 14 additions & 0 deletions include/swift/SIL/SILBuilder.h
Expand Up @@ -803,6 +803,20 @@ class SILBuilder {
operand, atomicity));
}

UnmanagedRetainValueInst *createUnmanagedRetainValue(SILLocation Loc,
SILValue operand) {
assert(F.hasQualifiedOwnership());
return insert(new (F.getModule()) UnmanagedRetainValueInst(
getSILDebugLocation(Loc), operand));
}

UnmanagedReleaseValueInst *createUnmanagedReleaseValue(SILLocation Loc,
SILValue operand) {
assert(F.hasQualifiedOwnership());
return insert(new (F.getModule()) UnmanagedReleaseValueInst(
getSILDebugLocation(Loc), operand));
}

CopyValueInst *createCopyValue(SILLocation Loc, SILValue operand) {
return insert(new (F.getModule())
CopyValueInst(getSILDebugLocation(Loc), operand));
Expand Down
18 changes: 18 additions & 0 deletions include/swift/SIL/SILCloner.h
Expand Up @@ -1144,6 +1144,15 @@ SILCloner<ImplClass>::visitRetainValueInst(RetainValueInst *Inst) {
Inst->getAtomicity()));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitUnmanagedRetainValueInst(
UnmanagedRetainValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
doPostProcess(
Inst, getBuilder().createUnmanagedRetainValue(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
Expand All @@ -1170,6 +1179,15 @@ void SILCloner<ImplClass>::visitReleaseValueInst(ReleaseValueInst *Inst) {
Inst->getAtomicity()));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitUnmanagedReleaseValueInst(
UnmanagedReleaseValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
doPostProcess(
Inst, getBuilder().createUnmanagedReleaseValue(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitDestroyValueInst(DestroyValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
Expand Down
26 changes: 26 additions & 0 deletions include/swift/SIL/SILInstruction.h
Expand Up @@ -2996,6 +2996,32 @@ class ReleaseValueInst : public UnaryInstructionBase<ValueKind::ReleaseValueInst
}
};

/// Copies a loadable value in an unmanaged, unbalanced way. Only meant for use
/// in ownership qualified SIL. Please do not use this EVER unless you are
/// implementing a part of the stdlib called Unmanaged.
class UnmanagedRetainValueInst
: public UnaryInstructionBase<ValueKind::UnmanagedRetainValueInst,
RefCountingInst,
/*HasValue*/ false> {
friend SILBuilder;

UnmanagedRetainValueInst(SILDebugLocation DebugLoc, SILValue operand)
: UnaryInstructionBase(DebugLoc, operand) {}
};

/// Destroys a loadable value in an unmanaged, unbalanced way. Only meant for
/// use in ownership qualified SIL. Please do not use this EVER unless you are
/// implementing a part of the stdlib called Unmanaged.
class UnmanagedReleaseValueInst
: public UnaryInstructionBase<ValueKind::UnmanagedReleaseValueInst,
RefCountingInst,
/*HasValue*/ false> {
friend SILBuilder;

UnmanagedReleaseValueInst(SILDebugLocation DebugLoc, SILValue operand)
: UnaryInstructionBase(DebugLoc, operand) {}
};

/// Transfers ownership of a loadable value to the current autorelease pool.
class AutoreleaseValueInst
: public UnaryInstructionBase<ValueKind::AutoreleaseValueInst,
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SIL/SILNodes.def
Expand Up @@ -158,6 +158,8 @@ ABSTRACT_VALUE(SILInstruction, ValueBase)
INST(MarkDependenceInst, SILInstruction, mark_dependence, None, DoesNotRelease)
INST(CopyBlockInst, SILInstruction, copy_block, MayHaveSideEffects, DoesNotRelease)
INST(CopyValueInst, SILInstruction, copy_value, MayHaveSideEffects, DoesNotRelease)
INST(UnmanagedRetainValueInst, RefCountingInst, unmanaged_retain_value, MayHaveSideEffects, DoesNotRelease)
INST(UnmanagedReleaseValueInst, RefCountingInst, unmanaged_release_value, MayHaveSideEffects, MayRelease)
INST(CopyUnownedValueInst, SILInstruction, copy_unowned_value, MayHaveSideEffects, DoesNotRelease)
INST(DestroyValueInst, SILInstruction, destroy_value, MayHaveSideEffects, MayRelease)

Expand Down
6 changes: 6 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Expand Up @@ -888,6 +888,12 @@ class IRGenSILFunction :
void visitStoreBorrowInst(StoreBorrowInst *i) {
llvm_unreachable("unimplemented");
}
void visitUnmanagedRetainValueInst(UnmanagedRetainValueInst *i) {
llvm_unreachable("unimplemented");
}
void visitUnmanagedReleaseValueInst(UnmanagedReleaseValueInst *i) {
llvm_unreachable("unimplemented");
}
void visitMarkDependenceInst(MarkDependenceInst *i);
void visitCopyBlockInst(CopyBlockInst *i);
void visitStrongPinInst(StrongPinInst *i);
Expand Down
2 changes: 2 additions & 0 deletions lib/Parse/ParseSIL.cpp
Expand Up @@ -1910,6 +1910,8 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB, SILBuilder &B) {
UNARY_INSTRUCTION(CopyUnownedValue)
UNARY_INSTRUCTION(DestroyValue)
UNARY_INSTRUCTION(CondFail)
UNARY_INSTRUCTION(UnmanagedReleaseValue)
UNARY_INSTRUCTION(UnmanagedRetainValue)
REFCOUNTING_INSTRUCTION(StrongPin)
REFCOUNTING_INSTRUCTION(StrongRetain)
REFCOUNTING_INSTRUCTION(StrongRelease)
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/SILOwnershipVerifier.cpp
Expand Up @@ -297,6 +297,8 @@ ACCEPTS_ANY_OWNERSHIP_INST(SelectEnum)
ACCEPTS_ANY_OWNERSHIP_INST(UncheckedBitwiseCast) // Is this right?
ACCEPTS_ANY_OWNERSHIP_INST(WitnessMethod) // Is this right?
ACCEPTS_ANY_OWNERSHIP_INST(ProjectBox) // The result is a T*.
ACCEPTS_ANY_OWNERSHIP_INST(UnmanagedRetainValue)
ACCEPTS_ANY_OWNERSHIP_INST(UnmanagedReleaseValue)
#undef ACCEPTS_ANY_OWNERSHIP_INST

// Trivial if trivial typed, otherwise must accept owned?
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/SILValue.cpp
Expand Up @@ -305,6 +305,8 @@ NO_RESULT_OWNERSHIP_INST(StrongRetain)
NO_RESULT_OWNERSHIP_INST(StrongRelease)
NO_RESULT_OWNERSHIP_INST(StrongRetainUnowned)
NO_RESULT_OWNERSHIP_INST(StrongUnpin)
NO_RESULT_OWNERSHIP_INST(UnmanagedRetainValue)
NO_RESULT_OWNERSHIP_INST(UnmanagedReleaseValue)
NO_RESULT_OWNERSHIP_INST(UnownedRetain)
NO_RESULT_OWNERSHIP_INST(UnownedRelease)
NO_RESULT_OWNERSHIP_INST(RetainValue)
Expand Down
21 changes: 21 additions & 0 deletions lib/SILOptimizer/Transforms/OwnershipModelEliminator.cpp
Expand Up @@ -64,6 +64,8 @@ struct OwnershipModelEliminatorVisitor
EBI->eraseFromParent();
return true;
}
bool visitUnmanagedRetainValueInst(UnmanagedRetainValueInst *URVI);
bool visitUnmanagedReleaseValueInst(UnmanagedReleaseValueInst *URVI);
};

} // end anonymous namespace
Expand Down Expand Up @@ -147,6 +149,25 @@ bool OwnershipModelEliminatorVisitor::visitCopyUnownedValueInst(
return true;
}

bool OwnershipModelEliminatorVisitor::visitUnmanagedRetainValueInst(
UnmanagedRetainValueInst *URVI) {
// Now that we have set the unqualified ownership flag, destroy value
// operation will delegate to the appropriate strong_release, etc.
B.emitCopyValueOperation(URVI->getLoc(), URVI->getOperand());
URVI->replaceAllUsesWith(URVI->getOperand());
URVI->eraseFromParent();
return true;
}

bool OwnershipModelEliminatorVisitor::visitUnmanagedReleaseValueInst(
UnmanagedReleaseValueInst *URVI) {
// Now that we have set the unqualified ownership flag, destroy value
// operation will delegate to the appropriate strong_release, etc.
B.emitDestroyValueOperation(URVI->getLoc(), URVI->getOperand());
URVI->eraseFromParent();
return true;
}

bool OwnershipModelEliminatorVisitor::visitDestroyValueInst(DestroyValueInst *DVI) {
// Now that we have set the unqualified ownership flag, destroy value
// operation will delegate to the appropriate strong_release, etc.
Expand Down
2 changes: 2 additions & 0 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Expand Up @@ -326,6 +326,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
case ValueKind::CopyBlockInst:
case ValueKind::CopyAddrInst:
case ValueKind::RetainValueInst:
case ValueKind::UnmanagedRetainValueInst:
case ValueKind::CopyValueInst:
case ValueKind::CopyUnownedValueInst:
case ValueKind::DeallocBoxInst:
Expand All @@ -340,6 +341,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
case ValueKind::ProjectBoxInst:
case ValueKind::ProjectExistentialBoxInst:
case ValueKind::ReleaseValueInst:
case ValueKind::UnmanagedReleaseValueInst:
case ValueKind::DestroyValueInst:
case ValueKind::AutoreleaseValueInst:
case ValueKind::DynamicMethodBranchInst:
Expand Down
2 changes: 2 additions & 0 deletions lib/Serialization/DeserializeSIL.cpp
Expand Up @@ -1323,10 +1323,12 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB,

UNARY_INSTRUCTION(CondFail)
REFCOUNTING_INSTRUCTION(RetainValue)
UNARY_INSTRUCTION(UnmanagedRetainValue)
UNARY_INSTRUCTION(CopyValue)
UNARY_INSTRUCTION(CopyUnownedValue)
UNARY_INSTRUCTION(DestroyValue)
REFCOUNTING_INSTRUCTION(ReleaseValue)
UNARY_INSTRUCTION(UnmanagedReleaseValue)
REFCOUNTING_INSTRUCTION(AutoreleaseValue)
REFCOUNTING_INSTRUCTION(SetDeallocating)
UNARY_INSTRUCTION(DeinitExistentialAddr)
Expand Down
2 changes: 2 additions & 0 deletions lib/Serialization/SerializeSIL.cpp
Expand Up @@ -1025,10 +1025,12 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
}
case ValueKind::CondFailInst:
case ValueKind::RetainValueInst:
case ValueKind::UnmanagedRetainValueInst:
case ValueKind::CopyValueInst:
case ValueKind::CopyUnownedValueInst:
case ValueKind::DestroyValueInst:
case ValueKind::ReleaseValueInst:
case ValueKind::UnmanagedReleaseValueInst:
case ValueKind::AutoreleaseValueInst:
case ValueKind::SetDeallocatingInst:
case ValueKind::DeallocStackInst:
Expand Down
27 changes: 21 additions & 6 deletions test/SIL/Parser/unmanaged.sil
@@ -1,10 +1,25 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil %s
// RUN: %target-sil-opt %s

import Swift
import Builtin

class C {}

sil @test : $@convention(thin) <U where U : AnyObject> (@inout Optional<U>) -> () {
bb0(%1 : $*Optional<U>):
%17 = load %1 : $*Optional<U>
%18 = ref_to_unmanaged %17 : $Optional<U> to $@sil_unmanaged Optional<U>
%6 = tuple ()
return %6 : $()
bb0(%0 : $*Optional<U>):
%1 = load [copy] %0 : $*Optional<U>
%2 = ref_to_unmanaged %1 : $Optional<U> to $@sil_unmanaged Optional<U>
%3 = unmanaged_to_ref %2 : $@sil_unmanaged Optional<U> to $Optional<U>
destroy_value %1 : $Optional<U>
%9999 = tuple ()
return %9999 : $()
}

sil @retain_release : $@convention(thin) (@sil_unmanaged Optional<C>) -> () {
bb0(%0 : $@sil_unmanaged Optional<C>):
%1 = unmanaged_to_ref %0 : $@sil_unmanaged Optional<C> to $Optional<C>
unmanaged_retain_value %1 : $Optional<C>
unmanaged_release_value %1 : $Optional<C>
%9999 = tuple()
return %9999 : $()
}
41 changes: 41 additions & 0 deletions test/SIL/Serialization/unmanaged.sil
@@ -0,0 +1,41 @@
// First parse this and then emit a *.sib. Then read in the *.sib, then recreate
// RUN: rm -rfv %t
// RUN: mkdir %t
// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name unmanaged
// RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.2.sib -module-name unmanaged
// RUN: %target-sil-opt %t/tmp.2.sib -module-name unmanaged | %FileCheck %s

import Swift
import Builtin

class C {}

// CHECK-LABEL: sil @retain_release : $@convention(thin) (@sil_unmanaged Optional<C>) -> () {
// CHECK: bb0([[ARG:%.*]] : $@sil_unmanaged Optional<C>):
// CHECK: [[REF:%.*]] = unmanaged_to_ref [[ARG]] : $@sil_unmanaged Optional<C> to $Optional<C>
// CHECK: unmanaged_retain_value [[REF]]
// CHECK: unmanaged_release_value [[REF]]
sil @retain_release : $@convention(thin) (@sil_unmanaged Optional<C>) -> () {
bb0(%0 : $@sil_unmanaged Optional<C>):
%1 = unmanaged_to_ref %0 : $@sil_unmanaged Optional<C> to $Optional<C>
unmanaged_retain_value %1 : $Optional<C>
unmanaged_release_value %1 : $Optional<C>
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @test : $@convention(thin) <U where U : AnyObject> (@inout Optional<U>) -> () {
// CHECK: bb0([[ARG:%.*]] : $*Optional<U>):
// CHECK: [[LOADED_ARG:%.*]] = load [copy] [[ARG]]
// CHECK: [[UNMANGED_LOADED_ARG:%.*]] = ref_to_unmanaged [[LOADED_ARG]] : $Optional<U> to $@sil_unmanaged Optional<U>
// CHECK: {{%.*}} = unmanaged_to_ref [[UNMANGED_LOADED_ARG]] : $@sil_unmanaged Optional<U> to $Optional<U>
// CHECK: destroy_value [[LOADED_ARG]]
sil @test : $@convention(thin) <U where U : AnyObject> (@inout Optional<U>) -> () {
bb0(%0 : $*Optional<U>):
%1 = load [copy] %0 : $*Optional<U>
%2 = ref_to_unmanaged %1 : $Optional<U> to $@sil_unmanaged Optional<U>
%3 = unmanaged_to_ref %2 : $@sil_unmanaged Optional<U> to $Optional<U>
destroy_value %1 : $Optional<U>
%9999 = tuple ()
return %9999 : $()
}
18 changes: 18 additions & 0 deletions test/SILOptimizer/ownership_model_eliminator.sil
Expand Up @@ -117,3 +117,21 @@ bb0(%0 : $@sil_unowned Builtin.NativeObject):
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @unmanaged_retain_release_test : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] : $Builtin.NativeObject):
// CHECK: strong_retain [[ARG]] : $Builtin.NativeObject
// CHECK: strong_release [[ARG]] : $Builtin.NativeObject
// CHECK: strong_release [[ARG]] : $Builtin.NativeObject
// CHECK: } // end sil function 'unmanaged_retain_release_test'
sil @unmanaged_retain_release_test : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
unmanaged_retain_value %0 : $Builtin.NativeObject
br bb1

bb1:
unmanaged_release_value %0 : $Builtin.NativeObject
destroy_value %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
1 change: 1 addition & 0 deletions utils/sil-mode.el
Expand Up @@ -108,6 +108,7 @@
"tuple_element_addr" "struct" "struct_extract"
"struct_element_addr" "ref_element_addr"
"autorelease_value" "copy_value" "destroy_value"
"unmanaged_retain_value" "unmanaged_release_value"
"copy_unowned_value")
'words) . font-lock-keyword-face)
;; Enums. *NOTE* We do not include enum itself here since enum is a
Expand Down

0 comments on commit 2da18c7

Please sign in to comment.