Skip to content

Commit 6bd4a8e

Browse files
committed
[semantic-arc] Eliminate unneeded unchecked_ownership_conversion from owned/guaranteed -> unowned.
Specifically, we check that all of the unowned's value uses are within the lifetime of the unchecked_ownership_conversion's operand. If so, we eliminate it.
1 parent 3ef21aa commit 6bd4a8e

File tree

6 files changed

+327
-1
lines changed

6 files changed

+327
-1
lines changed

lib/SILOptimizer/SemanticARC/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ target_sources(swiftSILOptimizer PRIVATE
77
OwnedToGuaranteedPhiOpt.cpp
88
Context.cpp
99
SemanticARCOptVisitor.cpp
10+
OwnershipConversionElimination.cpp
1011
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===--- OwnershipConversionElimination.cpp -------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "SemanticARC/SemanticARCOpts.h"
14+
#include "SemanticARCOptVisitor.h"
15+
#include "swift/SIL/LinearLifetimeChecker.h"
16+
17+
using namespace swift;
18+
using namespace semanticarc;
19+
20+
//===----------------------------------------------------------------------===//
21+
// Top Level Entrypoint
22+
//===----------------------------------------------------------------------===//
23+
24+
bool SemanticARCOptVisitor::visitUncheckedOwnershipConversionInst(
25+
UncheckedOwnershipConversionInst *uoci) {
26+
// Return false if we are supposed to only be running guaranteed opts.
27+
if (ctx.onlyGuaranteedOpts)
28+
return false;
29+
30+
// Then check if we are running tests and shouldn't perform this optimization
31+
// since we are testing something else.
32+
if (!ctx.shouldPerform(ARCTransformKind::OwnershipConversionElimPeephole))
33+
return false;
34+
35+
// Otherwise, shrink our state space so that we only consider conversions from
36+
// owned or guaranteed to unowned. These are always legal and it is sometimes
37+
// convenient to insert them to avoid forwarding issues when RAUWing
38+
// values. So we eliminate them here.
39+
if (uoci->getConversionOwnershipKind() != OwnershipKind::Unowned)
40+
return false;
41+
42+
auto op = uoci->getOperand();
43+
auto opKind = op.getOwnershipKind();
44+
if (opKind != OwnershipKind::Owned && opKind != OwnershipKind::Guaranteed)
45+
return false;
46+
47+
// Ok, we can perform our optimization. First go through all of the uses of
48+
// uoci and see if they can accept our operand without any changes and that
49+
// they do not consume values.
50+
SmallVector<Operand *, 8> newUses;
51+
for (auto *use : uoci->getUses()) {
52+
if (use->isLifetimeEnding() || !use->canAcceptKind(opKind))
53+
return false;
54+
newUses.push_back(use);
55+
}
56+
57+
// Ok, now we need to perform our lifetime check.
58+
SmallVector<Operand *, 8> consumingUses(op->getConsumingUses());
59+
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
60+
LinearLifetimeChecker checker(visitedBlocks, ctx.getDeadEndBlocks());
61+
if (!checker.validateLifetime(op, consumingUses, newUses))
62+
return false;
63+
64+
// Otherwise, we can perform our rauw.
65+
eraseAndRAUWSingleValueInstruction(uoci, op);
66+
return true;
67+
}

lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
148148
bool visitCopyValueInst(CopyValueInst *cvi);
149149
bool visitBeginBorrowInst(BeginBorrowInst *bbi);
150150
bool visitLoadInst(LoadInst *li);
151+
bool
152+
visitUncheckedOwnershipConversionInst(UncheckedOwnershipConversionInst *uoci);
151153

152154
static bool shouldVisitInst(SILInstruction *i) {
153155
switch (i->getKind()) {
@@ -156,6 +158,7 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
156158
case SILInstructionKind::CopyValueInst:
157159
case SILInstructionKind::BeginBorrowInst:
158160
case SILInstructionKind::LoadInst:
161+
case SILInstructionKind::UncheckedOwnershipConversionInst:
159162
return true;
160163
}
161164
}

lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ static llvm::cl::list<ARCTransformKind> TransformsToPerform(
4040
clEnumValN(ARCTransformKind::LifetimeJoiningPeephole,
4141
"sil-semantic-arc-peepholes-lifetime-joining",
4242
"Perform the join lifetimes peephole"),
43+
clEnumValN(ARCTransformKind::OwnershipConversionElimPeephole,
44+
"sil-semantic-arc-peepholes-ownership-conversion-elim",
45+
"Eliminate unchecked_ownership_conversion insts that are "
46+
"not needed"),
4347
clEnumValN(ARCTransformKind::OwnedToGuaranteedPhi,
4448
"sil-semantic-arc-owned-to-guaranteed-phi",
4549
"Perform Owned To Guaranteed Phi. NOTE: Seeded by peephole "
@@ -78,6 +82,7 @@ struct SemanticARCOpts : SILFunctionTransform {
7882
case ARCTransformKind::RedundantBorrowScopeElimPeephole:
7983
case ARCTransformKind::LoadCopyToLoadBorrowPeephole:
8084
case ARCTransformKind::AllPeepholes:
85+
case ARCTransformKind::OwnershipConversionElimPeephole:
8186
// We never assume we are at fixed point when running these transforms.
8287
if (performPeepholesWithoutFixedPoint(visitor)) {
8388
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);

lib/SILOptimizer/SemanticARC/SemanticARCOpts.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ enum class ARCTransformKind : uint64_t {
3030
// such as dead live range, guaranteed copy_value opt, etc.
3131
RedundantCopyValueElimPeephole = 0x8,
3232
LifetimeJoiningPeephole = 0x10,
33+
OwnershipConversionElimPeephole = 0x20,
3334

3435
AllPeepholes = LoadCopyToLoadBorrowPeephole |
3536
RedundantBorrowScopeElimPeephole |
36-
RedundantCopyValueElimPeephole | LifetimeJoiningPeephole,
37+
RedundantCopyValueElimPeephole | LifetimeJoiningPeephole |
38+
OwnershipConversionElimPeephole,
3739
All = AllPeepholes | OwnedToGuaranteedPhi,
3840
};
3941

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// RUN: %target-sil-opt -module-name Swift -enable-sil-verify-all -semantic-arc-opts -sil-semantic-arc-peepholes-ownership-conversion-elim -sil-semantic-arc-peepholes-lifetime-joining %s | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
7+
//////////////////
8+
// Declarations //
9+
//////////////////
10+
11+
typealias AnyObject = Builtin.AnyObject
12+
13+
enum MyNever {}
14+
enum FakeOptional<T> {
15+
case none
16+
case some(T)
17+
}
18+
19+
sil [ossa] @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
20+
sil [ossa] @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
21+
sil [ossa] @unowned_user : $@convention(thin) (Builtin.NativeObject) -> ()
22+
sil [ossa] @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
23+
sil [ossa] @unreachable_guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever
24+
sil [ossa] @inout_user : $@convention(thin) (@inout FakeOptional<NativeObjectPair>) -> ()
25+
sil [ossa] @get_native_object : $@convention(thin) () -> @owned Builtin.NativeObject
26+
27+
struct NativeObjectPair {
28+
var obj1 : Builtin.NativeObject
29+
var obj2 : Builtin.NativeObject
30+
}
31+
32+
sil [ossa] @get_object_pair : $@convention(thin) () -> @owned NativeObjectPair
33+
34+
struct FakeOptionalNativeObjectPairPair {
35+
var pair1 : FakeOptional<NativeObjectPair>
36+
var pair2 : FakeOptional<NativeObjectPair>
37+
}
38+
sil [ossa] @inout_user2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
39+
40+
sil [ossa] @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair
41+
sil [ossa] @consume_nativeobject_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
42+
43+
protocol MyFakeAnyObject : Klass {
44+
func myFakeMethod()
45+
}
46+
47+
final class Klass {
48+
var base: Klass
49+
let baseLet: Klass
50+
}
51+
52+
extension Klass : MyFakeAnyObject {
53+
func myFakeMethod()
54+
}
55+
sil [ossa] @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
56+
sil [ossa] @unowned_klass_user : $@convention(thin) (Klass) -> ()
57+
sil [ossa] @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()
58+
sil [ossa] @guaranteed_fakeoptional_classlet_user : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> ()
59+
60+
struct MyInt {
61+
var value: Builtin.Int32
62+
}
63+
64+
struct StructWithDataAndOwner {
65+
var data : Builtin.Int32
66+
var owner : Klass
67+
}
68+
69+
struct StructMemberTest {
70+
var c : Klass
71+
var s : StructWithDataAndOwner
72+
var t : (Builtin.Int32, StructWithDataAndOwner)
73+
}
74+
75+
class ClassLet {
76+
@_hasStorage let aLet: Klass
77+
@_hasStorage var aVar: Klass
78+
@_hasStorage let aLetTuple: (Klass, Klass)
79+
@_hasStorage let anOptionalLet: FakeOptional<Klass>
80+
81+
@_hasStorage let anotherLet: ClassLet
82+
}
83+
84+
class SubclassLet: ClassLet {}
85+
86+
sil_global [let] @a_let_global : $Klass
87+
sil_global @a_var_global : $Klass
88+
89+
enum EnumWithIndirectCase {
90+
case first
91+
indirect case second(Builtin.NativeObject)
92+
}
93+
94+
struct StructWithEnumWithIndirectCaseField {
95+
var i: Builtin.Int23
96+
var field : EnumWithIndirectCase
97+
}
98+
99+
sil [ossa] @get_fakeoptional_nativeobject : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject>
100+
101+
struct NativeObjectWrapper {
102+
var innerWrapper : Builtin.NativeObject
103+
}
104+
105+
sil @owned_user_object_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
106+
107+
///////////
108+
// Tests //
109+
///////////
110+
111+
// CHECK-LABEL: sil [ossa] @guaranteed_to_unowned_positive_1 : $@convention(thin) (@owned Klass) -> () {
112+
// CHECK-NOT: unchecked_ownership_conversion
113+
// CHECK: } // end sil function 'guaranteed_to_unowned_positive_1'
114+
sil [ossa] @guaranteed_to_unowned_positive_1 : $@convention(thin) (@owned Klass) -> () {
115+
bb0(%0 : @owned $Klass):
116+
%1 = begin_borrow %0 : $Klass
117+
%2 = unchecked_ownership_conversion %1 : $Klass, @guaranteed to @unowned
118+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
119+
apply %func(%2) : $@convention(thin) (Klass) -> ()
120+
end_borrow %1 : $Klass
121+
destroy_value %0 : $Klass
122+
%9999 = tuple()
123+
return %9999 : $()
124+
}
125+
126+
// CHECK-LABEL: sil [ossa] @guaranteed_to_unowned_unreachable : $@convention(thin) (@owned Klass) -> () {
127+
// CHECK: begin_borrow
128+
// CHECK-NOT: unchecked_ownership_conversion
129+
// CHECK: } // end sil function 'guaranteed_to_unowned_unreachable'
130+
sil [ossa] @guaranteed_to_unowned_unreachable : $@convention(thin) (@owned Klass) -> () {
131+
bb0(%0 : @owned $Klass):
132+
%1 = begin_borrow %0 : $Klass
133+
%2 = unchecked_ownership_conversion %1 : $Klass, @guaranteed to @unowned
134+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
135+
apply %func(%2) : $@convention(thin) (Klass) -> ()
136+
unreachable
137+
}
138+
139+
// CHECK-LABEL: sil [ossa] @guaranteed_to_unowned_unreachable_2 : $@convention(thin) (@owned Klass) -> () {
140+
// CHECK-NOT: unchecked_ownership_conversion
141+
// CHECK: } // end sil function 'guaranteed_to_unowned_unreachable_2'
142+
sil [ossa] @guaranteed_to_unowned_unreachable_2 : $@convention(thin) (@owned Klass) -> () {
143+
bb0(%0 : @owned $Klass):
144+
%1 = begin_borrow %0 : $Klass
145+
%2 = unchecked_ownership_conversion %1 : $Klass, @guaranteed to @unowned
146+
cond_br undef, bb1, bb2
147+
148+
bb1:
149+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
150+
apply %func(%2) : $@convention(thin) (Klass) -> ()
151+
end_borrow %1 : $Klass
152+
br bb3
153+
154+
bb2:
155+
unreachable
156+
157+
bb3:
158+
destroy_value %0 : $Klass
159+
%9999 = tuple()
160+
return %9999 : $()
161+
}
162+
163+
// Borrow scope is too small, we fail.
164+
//
165+
// CHECK-LABEL: sil [ossa] @guaranteed_to_unowned_negative_1 : $@convention(thin) (@owned Klass) -> () {
166+
// CHECK: unchecked_ownership_conversion
167+
// CHECK: } // end sil function 'guaranteed_to_unowned_negative_1'
168+
sil [ossa] @guaranteed_to_unowned_negative_1 : $@convention(thin) (@owned Klass) -> () {
169+
bb0(%0 : @owned $Klass):
170+
%1 = begin_borrow %0 : $Klass
171+
%2 = unchecked_ownership_conversion %1 : $Klass, @guaranteed to @unowned
172+
end_borrow %1 : $Klass
173+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
174+
apply %func(%2) : $@convention(thin) (Klass) -> ()
175+
destroy_value %0 : $Klass
176+
%9999 = tuple()
177+
return %9999 : $()
178+
}
179+
180+
/////////////////
181+
// Owned Tests //
182+
/////////////////
183+
184+
// CHECK-LABEL: sil [ossa] @owned_to_unowned_positive_1 : $@convention(thin) (@guaranteed Klass) -> () {
185+
// CHECK-NOT: unchecked_ownership_conversion
186+
// CHECK: } // end sil function 'owned_to_unowned_positive_1'
187+
sil [ossa] @owned_to_unowned_positive_1 : $@convention(thin) (@guaranteed Klass) -> () {
188+
bb0(%0 : @guaranteed $Klass):
189+
%1 = copy_value %0 : $Klass
190+
%2 = unchecked_ownership_conversion %1 : $Klass, @owned to @unowned
191+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
192+
apply %func(%2) : $@convention(thin) (Klass) -> ()
193+
destroy_value %1 : $Klass
194+
%9999 = tuple()
195+
return %9999 : $()
196+
}
197+
198+
// CHECK-LABEL: sil [ossa] @owned_to_unowned_unreachable : $@convention(thin) (@guaranteed Klass) -> () {
199+
// CHECK: copy_value
200+
// CHECK-NOT: unchecked_ownership_conversion
201+
// CHECK: } // end sil function 'owned_to_unowned_unreachable'
202+
sil [ossa] @owned_to_unowned_unreachable : $@convention(thin) (@guaranteed Klass) -> () {
203+
bb0(%0 : @guaranteed $Klass):
204+
%1 = copy_value %0 : $Klass
205+
%2 = unchecked_ownership_conversion %1 : $Klass, @owned to @unowned
206+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
207+
apply %func(%2) : $@convention(thin) (Klass) -> ()
208+
unreachable
209+
}
210+
211+
// CHECK-LABEL: sil [ossa] @owned_to_unowned_unreachable_2 : $@convention(thin) (@guaranteed Klass) -> () {
212+
// CHECK-NOT: unchecked_ownership_conversion
213+
// CHECK: } // end sil function 'owned_to_unowned_unreachable_2'
214+
sil [ossa] @owned_to_unowned_unreachable_2 : $@convention(thin) (@guaranteed Klass) -> () {
215+
bb0(%0 : @guaranteed $Klass):
216+
%1 = copy_value %0 : $Klass
217+
%2 = unchecked_ownership_conversion %1 : $Klass, @owned to @unowned
218+
cond_br undef, bb1, bb2
219+
220+
bb1:
221+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
222+
apply %func(%2) : $@convention(thin) (Klass) -> ()
223+
destroy_value %1 : $Klass
224+
br bb3
225+
226+
bb2:
227+
unreachable
228+
229+
bb3:
230+
%9999 = tuple()
231+
return %9999 : $()
232+
}
233+
234+
// Borrow scope is too small, we fail.
235+
//
236+
// CHECK-LABEL: sil [ossa] @owned_to_unowned_negative_1 : $@convention(thin) (@guaranteed Klass) -> () {
237+
// CHECK: unchecked_ownership_conversion
238+
// CHECK: } // end sil function 'owned_to_unowned_negative_1'
239+
sil [ossa] @owned_to_unowned_negative_1 : $@convention(thin) (@guaranteed Klass) -> () {
240+
bb0(%0 : @guaranteed $Klass):
241+
%1 = copy_value %0 : $Klass
242+
%2 = unchecked_ownership_conversion %1 : $Klass, @owned to @unowned
243+
destroy_value %1 : $Klass
244+
%func = function_ref @unowned_klass_user : $@convention(thin) (Klass) -> ()
245+
apply %func(%2) : $@convention(thin) (Klass) -> ()
246+
%9999 = tuple()
247+
return %9999 : $()
248+
}

0 commit comments

Comments
 (0)