Skip to content

Commit e9896fb

Browse files
committed
[ownership] Change init_existential_ref to be forwarding for both guaranteed and owned.
Otherwise in call frames like the one in the test in this commit get unneeded ARC traffic. We should never pessimize read only code that doesnt need side-effects with side-effects if we can avoid it. I am seeing this a bunch when I look at SIL from projects that use a lot of protocols. Specifically, one has a sort of trampoline code that wraps a ref counted object in an existential ref container (which from an ARC perspective doesn't imply ownership) and then calls a method on it or passes it off to some other code. Because of this requirement, there is a copy/destroy that can not be eliminated unless we can devirt/inline/eliminate the init_existential_ref box, inline enough that the low level ARC optimizer can hit it. We shouldn't rely on such properties if we do not need to.
1 parent 3a9969c commit e9896fb

File tree

8 files changed

+58
-37
lines changed

8 files changed

+58
-37
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6192,7 +6192,7 @@ class InitExistentialValueInst final
61926192
class InitExistentialRefInst final
61936193
: public UnaryInstructionWithTypeDependentOperandsBase<
61946194
SILInstructionKind::InitExistentialRefInst, InitExistentialRefInst,
6195-
SingleValueInstruction> {
6195+
OwnershipForwardingSingleValueInst> {
61966196
friend SILBuilder;
61976197

61986198
CanType ConcreteType;
@@ -6203,7 +6203,8 @@ class InitExistentialRefInst final
62036203
ArrayRef<SILValue> TypeDependentOperands,
62046204
ArrayRef<ProtocolConformanceRef> Conformances)
62056205
: UnaryInstructionWithTypeDependentOperandsBase(
6206-
DebugLoc, Instance, TypeDependentOperands, ExistentialType),
6206+
DebugLoc, Instance, TypeDependentOperands, ExistentialType,
6207+
Instance.getOwnershipKind()),
62076208
ConcreteType(FormalConcreteType), Conformances(Conformances) {}
62086209

62096210
static InitExistentialRefInst *

lib/SIL/OperandOwnership.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@ CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocExistentialBox)
173173
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocRef)
174174
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DestroyValue)
175175
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndLifetime)
176-
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, InitExistentialRef)
177176
CONSTANT_OWNERSHIP_INST(None, MustBeLive, AbortApply)
178177
CONSTANT_OWNERSHIP_INST(None, MustBeLive, AddressToPointer)
179178
CONSTANT_OWNERSHIP_INST(None, MustBeLive, BeginAccess)
@@ -348,6 +347,7 @@ FORWARD_ANY_OWNERSHIP_INST(UnconditionalCheckedCast)
348347
FORWARD_ANY_OWNERSHIP_INST(UncheckedEnumData)
349348
FORWARD_ANY_OWNERSHIP_INST(DestructureStruct)
350349
FORWARD_ANY_OWNERSHIP_INST(DestructureTuple)
350+
FORWARD_ANY_OWNERSHIP_INST(InitExistentialRef)
351351
#undef FORWARD_ANY_OWNERSHIP_INST
352352

353353
// An instruction that forwards a constant ownership or trivial ownership.

lib/SIL/OwnershipUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ bool swift::isOwnershipForwardingValueKind(SILNodeKind kind) {
4646
case SILNodeKind::DestructureStructInst:
4747
case SILNodeKind::DestructureTupleInst:
4848
case SILNodeKind::MarkDependenceInst:
49+
case SILNodeKind::InitExistentialRefInst:
4950
return true;
5051
default:
5152
return false;

lib/SIL/ValueOwnership.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,6 @@ CONSTANT_OWNERSHIP_INST(Owned, KeyPath)
7878
CONSTANT_OWNERSHIP_INST(Owned, InitExistentialValue)
7979
CONSTANT_OWNERSHIP_INST(Owned, GlobalValue) // TODO: is this correct?
8080

81-
// NOTE: Even though init_existential_ref from a reference counting perspective
82-
// is not considered to be "owned" since it doesn't affect reference counts,
83-
// conceptually we want to treat it as an owned value that produces owned
84-
// things, rather than a forwarding thing since initialization is generally a
85-
// consuming operation.
86-
CONSTANT_OWNERSHIP_INST(Owned, InitExistentialRef)
87-
8881
// One would think that these /should/ be unowned. In truth they are owned since
8982
// objc metatypes do not go through the retain/release fast path. In their
9083
// implementations of retain/release nothing happens, so this is safe.
@@ -260,6 +253,16 @@ FORWARDING_OWNERSHIP_INST(Upcast)
260253
FORWARDING_OWNERSHIP_INST(UncheckedEnumData)
261254
FORWARDING_OWNERSHIP_INST(SelectEnum)
262255
FORWARDING_OWNERSHIP_INST(Enum)
256+
// NOTE: init_existential_ref from a reference counting perspective is not
257+
// considered to be "owned" since it doesn't affect reference counts. That being
258+
// said in the past, we wanted to conceptually treat it as an owned value that
259+
// produces owned things, rather than a forwarding thing since initialization is
260+
// generally a consuming operation. That being said, there are often cases in
261+
// class based code where we are propagating around a plus zero version of a
262+
// value and need to wrap the class in an existential wrapper in an intermediate
263+
// frame from usage. In such cases, we have been creating unnecessary ref count
264+
// traffic in code.
265+
FORWARDING_OWNERSHIP_INST(InitExistentialRef)
263266
#undef FORWARDING_OWNERSHIP_INST
264267

265268
ValueOwnershipKind

lib/SILOptimizer/Transforms/SemanticARCOpts.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ struct SemanticARCOptVisitor
593593
FORWARDING_INST(OpenExistentialValue)
594594
FORWARDING_INST(OpenExistentialBoxValue)
595595
FORWARDING_INST(MarkDependence)
596+
FORWARDING_INST(InitExistentialRef)
596597
#undef FORWARDING_INST
597598

598599
#define FORWARDING_TERM(NAME) \

test/SIL/ownership-verifier/over_consume.sil

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -460,32 +460,6 @@ bb0(%0 : @owned $Klass):
460460
return %9999 : $()
461461
}
462462

463-
// CHECK-LABEL: Function: 'init_existential_ref_not_forwarding'
464-
// CHECK: Have operand with incompatible ownership?!
465-
// CHECK: Value: %0 = argument of bb0 : $ClassProtConformingRef
466-
// CHECK: User: %2 = init_existential_ref %0 : $ClassProtConformingRef : $ClassProtConformingRef, $ClassProt
467-
// CHECK: Operand Number: 0
468-
// CHECK: Conv: guaranteed
469-
// CHECK: OwnershipMap:
470-
// CHECK: -- OperandOwnershipKindMap --
471-
// CHECK: unowned: No.
472-
// CHECK: owned: Yes. Liveness: MustBeInvalidated
473-
// CHECK: guaranteed: No.
474-
// CHECK: any: Yes. Liveness: MustBeLive
475-
// CHECK-NOT: init_existential_ref %1
476-
// CHECK: Function: 'init_existential_ref_not_forwarding'
477-
// CHECK: Error! Found a leaked owned value that was never consumed.
478-
// CHECK: Value: %2 = init_existential_ref %0 : $ClassProtConformingRef : $ClassProtConformingRef, $ClassProt
479-
// CHECK-NOT: init_existential_ref %1
480-
sil [ossa] @init_existential_ref_not_forwarding : $@convention(thin) (@guaranteed ClassProtConformingRef, @owned ClassProtConformingRef) -> @owned (ClassProt, ClassProt) {
481-
bb0(%0 : @guaranteed $ClassProtConformingRef, %1 : @owned $ClassProtConformingRef):
482-
%2 = init_existential_ref %0 : $ClassProtConformingRef : $ClassProtConformingRef, $ClassProt
483-
%3 = copy_value %2 : $ClassProt
484-
%4 = init_existential_ref %1 : $ClassProtConformingRef : $ClassProtConformingRef, $ClassProt
485-
%5 = tuple(%3 : $ClassProt, %4 : $ClassProt)
486-
return %5 : $(ClassProt, ClassProt)
487-
}
488-
489463
sil [ossa] @eliminate_copy_try_apple_callee : $@convention(thin) (@owned Builtin.NativeObject) -> @error Error {
490464
entry(%0 : @owned $Builtin.NativeObject):
491465
%9999 = tuple()

test/SIL/ownership-verifier/use_verifier.sil

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ class UnsafeUnownedFieldKlass {
109109
unowned(safe) var x: @sil_unowned Builtin.NativeObject
110110
}
111111

112+
class ClassProtConformingRef {}
113+
protocol ClassProt : AnyObject {}
114+
extension ClassProtConformingRef : ClassProt {}
115+
112116
////////////////
113117
// Test Cases //
114118
////////////////
@@ -1292,3 +1296,12 @@ bb4:
12921296
destroy_value %1 : $Builtin.NativeObject
12931297
return %9999 : $()
12941298
}
1299+
1300+
sil [ossa] @init_existential_ref_forwarding : $@convention(thin) (@guaranteed ClassProtConformingRef, @owned ClassProtConformingRef) -> @owned (ClassProt, ClassProt) {
1301+
bb0(%0 : @guaranteed $ClassProtConformingRef, %1 : @owned $ClassProtConformingRef):
1302+
%2 = init_existential_ref %0 : $ClassProtConformingRef : $ClassProtConformingRef, $ClassProt
1303+
%3 = copy_value %2 : $ClassProt
1304+
%4 = init_existential_ref %1 : $ClassProtConformingRef : $ClassProtConformingRef, $ClassProt
1305+
%5 = tuple(%3 : $ClassProt, %4 : $ClassProt)
1306+
return %5 : $(ClassProt, ClassProt)
1307+
}

test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import Builtin
88
// Declarations //
99
//////////////////
1010

11+
typealias AnyObject = Builtin.AnyObject
12+
1113
enum MyNever {}
1214
enum FakeOptional<T> {
1315
case none
@@ -26,7 +28,14 @@ struct NativeObjectPair {
2628

2729
sil @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair
2830

29-
class Klass {}
31+
protocol MyFakeAnyObject : Klass {
32+
func myFakeMethod()
33+
}
34+
35+
final class Klass {}
36+
extension Klass : MyFakeAnyObject {
37+
func myFakeMethod()
38+
}
3039
sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
3140
sil @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()
3241
sil @guaranteed_fakeoptional_classlet_user : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> ()
@@ -1781,3 +1790,22 @@ bb0(%0 : @guaranteed $ClassLet):
17811790
%9999 = tuple()
17821791
return %9999 : $()
17831792
}
1793+
1794+
// Make sure we can chew through this and get rid of all ARC traffic.
1795+
// CHECK-LABEL: sil [ossa] @init_existential_ref_forwarding_test : $@convention(thin) (@guaranteed Klass) -> () {
1796+
// CHECK-NOT: copy_value
1797+
// CHECK-NOT: begin_borrow
1798+
// CHECK: } // end sil function 'init_existential_ref_forwarding_test'
1799+
sil [ossa] @init_existential_ref_forwarding_test : $@convention(thin) (@guaranteed Klass) -> () {
1800+
bb0(%0 : @guaranteed $Klass):
1801+
%0a = copy_value %0 : $Klass
1802+
%1 = init_existential_ref %0a : $Klass : $Klass, $MyFakeAnyObject
1803+
%1a = begin_borrow %1 : $MyFakeAnyObject
1804+
%2 = open_existential_ref %1a : $MyFakeAnyObject to $@opened("A2E21C52-6089-11E4-9866-3C0754723233") MyFakeAnyObject
1805+
%3 = witness_method $@opened("A2E21C52-6089-11E4-9866-3C0754723233") MyFakeAnyObject, #MyFakeAnyObject.myFakeMethod!1, %2 : $@opened("A2E21C52-6089-11E4-9866-3C0754723233") MyFakeAnyObject : $@convention(witness_method: MyFakeAnyObject) <τ_0_0 where τ_0_0 : MyFakeAnyObject> (@guaranteed τ_0_0) -> ()
1806+
apply %3<@opened("A2E21C52-6089-11E4-9866-3C0754723233") MyFakeAnyObject>(%2) : $@convention(witness_method: MyFakeAnyObject) <τ_0_0 where τ_0_0 : MyFakeAnyObject> (@guaranteed τ_0_0) -> ()
1807+
end_borrow %1a : $MyFakeAnyObject
1808+
destroy_value %1 : $MyFakeAnyObject
1809+
%9999 = tuple()
1810+
return %9999 : $()
1811+
}

0 commit comments

Comments
 (0)