Skip to content

Commit 02cff7e

Browse files
committed
[SAO] Add RedundantMoveValueElimination.
Adds to SemanticARCOpts a new step of removing move_value instructions if they are redundant. A move_value is redundant if it adds no new information or optimization opportunities. An example of adding information: a lifetime becoming lexical. The new lifetime's destroys cannot be hoisted over deinit barriers. An example of adding an optimization opportunity: the original value escapes but the value produced by the move_value does not escape. So destroys of the new value can be hoisted more aggressively.
1 parent b99a140 commit 02cff7e

File tree

6 files changed

+268
-2
lines changed

6 files changed

+268
-2
lines changed

lib/SILOptimizer/SemanticARC/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ target_sources(swiftSILOptimizer PRIVATE
88
Context.cpp
99
SemanticARCOptVisitor.cpp
1010
OwnershipConversionElimination.cpp
11+
RedundantMoveValueElimination.cpp
1112
)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===--- OwnershipConversionElimination.cpp -------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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+
// A move_value ends an owned lifetime and begins an owned lifetime.
13+
//
14+
// The new lifetime may have the same characteristics as the original lifetime
15+
// with regards to
16+
// - lexicality
17+
// - escaping
18+
//
19+
// If it has the same characteristics, there is no reason to have two separate
20+
// lifetimes--they are redundant. This optimization deletes such redundant
21+
// move_values.
22+
//===----------------------------------------------------------------------===//
23+
24+
#include "SemanticARC/SemanticARCOpts.h"
25+
#include "SemanticARCOptVisitor.h"
26+
#include "swift/SIL/LinearLifetimeChecker.h"
27+
28+
using namespace swift;
29+
using namespace semanticarc;
30+
31+
//===----------------------------------------------------------------------===//
32+
// Top Level Entrypoint
33+
//===----------------------------------------------------------------------===//
34+
35+
bool SemanticARCOptVisitor::visitMoveValueInst(MoveValueInst *mvi) {
36+
if (ctx.onlyMandatoryOpts)
37+
return false;
38+
39+
if (!ctx.shouldPerform(ARCTransformKind::RedundantMoveValueElim))
40+
return false;
41+
42+
auto original = mvi->getOperand();
43+
44+
// First, check whether lexicality matches, the cheaper check.
45+
if (mvi->isLexical() != original->isLexical()) {
46+
return false;
47+
}
48+
49+
// Then, check whether escaping matches, the more expensive check.
50+
if (hasPointerEscape(mvi) != hasPointerEscape(original)) {
51+
return false;
52+
}
53+
54+
// Both characteristics match.
55+
eraseAndRAUWSingleValueInstruction(mvi, original);
56+
return true;
57+
}

lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
143143
bool visitCopyValueInst(CopyValueInst *cvi);
144144
bool visitBeginBorrowInst(BeginBorrowInst *bbi);
145145
bool visitLoadInst(LoadInst *li);
146+
bool visitMoveValueInst(MoveValueInst *mvi);
146147
bool
147148
visitUncheckedOwnershipConversionInst(UncheckedOwnershipConversionInst *uoci);
148149

@@ -153,6 +154,7 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
153154
case SILInstructionKind::CopyValueInst:
154155
case SILInstructionKind::BeginBorrowInst:
155156
case SILInstructionKind::LoadInst:
157+
case SILInstructionKind::MoveValueInst:
156158
case SILInstructionKind::UncheckedOwnershipConversionInst:
157159
return true;
158160
}

lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ static llvm::cl::list<ARCTransformKind> TransformsToPerform(
5151
"sil-semantic-arc-owned-to-guaranteed-phi",
5252
"Perform Owned To Guaranteed Phi. NOTE: Seeded by peephole "
5353
"optimizer for compile time saving purposes, so run this "
54-
"after running peepholes)")),
54+
"after running peepholes)"),
55+
clEnumValN(ARCTransformKind::RedundantMoveValueElim,
56+
"sil-semantic-arc-redundant-move-value-elim",
57+
"Eliminate move_value which don't change owned lifetime "
58+
"characteristics. (Escaping, Lexical).")),
5559
llvm::cl::desc(
5660
"For testing purposes only run the specified list of semantic arc "
5761
"optimization. If the list is empty, we run all transforms"));
@@ -86,6 +90,7 @@ struct SemanticARCOpts : SILFunctionTransform {
8690
case ARCTransformKind::LoadCopyToLoadBorrowPeephole:
8791
case ARCTransformKind::AllPeepholes:
8892
case ARCTransformKind::OwnershipConversionElimPeephole:
93+
case ARCTransformKind::RedundantMoveValueElim:
8994
// We never assume we are at fixed point when running these transforms.
9095
if (performPeepholesWithoutFixedPoint(visitor)) {
9196
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);

lib/SILOptimizer/SemanticARC/SemanticARCOpts.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ enum class ARCTransformKind : uint64_t {
3131
RedundantCopyValueElimPeephole = 0x8,
3232
LifetimeJoiningPeephole = 0x10,
3333
OwnershipConversionElimPeephole = 0x20,
34+
RedundantMoveValueElim = 0x40,
3435

3536
AllPeepholes = LoadCopyToLoadBorrowPeephole |
3637
RedundantBorrowScopeElimPeephole |
3738
RedundantCopyValueElimPeephole | LifetimeJoiningPeephole |
3839
OwnershipConversionElimPeephole,
39-
All = AllPeepholes | OwnedToGuaranteedPhi,
40+
All = AllPeepholes | OwnedToGuaranteedPhi | RedundantMoveValueElim,
4041
};
4142

4243
inline ARCTransformKind operator&(ARCTransformKind lhs, ARCTransformKind rhs) {
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// RUN: %target-sil-opt -module-name Swift -enable-sil-verify-all -semantic-arc-opts -sil-semantic-arc-redundant-move-value-elim %s | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
7+
class C {}
8+
9+
sil @getOwned : $@convention(thin) () -> (@owned C)
10+
11+
sil @borrow : $@convention(thin) (@guaranteed C) -> ()
12+
13+
sil @useUnmanaged : $@convention(thin) (@sil_unmanaged C) -> ()
14+
15+
// Test that move_value instructions are removed when they are "redundant". A
16+
// move_value instruction is redundant when the lifetime it introduces has the
17+
// same characteristics as the lifetime that it ends along in the following two
18+
// aspects:
19+
// - lexicaliity
20+
// - escapingness
21+
22+
// The tests are named as follows:
23+
//
24+
// @test_{old_characteristics}_{new_characteristics}
25+
// where both old_characteristics and new_characteristics are of the form
26+
//
27+
// {is_lexical}{has_escaping_use}
28+
//
29+
// and both is_lexical and has_escaping_use are 1 or 0 depending on whether each
30+
// is true.
31+
//
32+
// So for example, in @test_00_10, there is a move_value instruction which ends
33+
// a lifetime that is both neither lexical nor escaping and begins a lifetime
34+
// which is lexical but not escaping. Since the characteristics of the old and
35+
// new lifetimes differ, the move_value should be preserved.
36+
37+
// Note that these tests all have two move_values. That's just to make it a bit
38+
// easier to specify the characteristics of the first lifetime. The move_value
39+
// of real interest for the tests is the second.
40+
41+
// Old: lexical , non-escaping
42+
// New: lexical , non-escaping
43+
// Same. Redundant. Remove move_value.
44+
//
45+
// CHECK-LABEL: sil [ossa] @test_10_10 : {{.*}} {
46+
// CHECK: [[INSTANCE:%[^,]+]] = apply
47+
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
48+
// CHECK-NOT: move_value
49+
// CHECK-LABEL: } // end sil function 'test_10_10'
50+
sil [ossa] @test_10_10 : $@convention(thin) () -> () {
51+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
52+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
53+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
54+
%lifetime = move_value [lexical] %instance : $C
55+
apply %borrow(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
56+
%lifetime2 = move_value [lexical] %lifetime : $C
57+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
58+
destroy_value %lifetime2 : $C
59+
%retval = tuple ()
60+
return %retval : $()
61+
}
62+
63+
// Old: lexical , non-escaping
64+
// New: non-lexical, non-escaping
65+
// Different. Non-redundant. Keep move_value.
66+
//
67+
// CHECK-LABEL: sil [ossa] @test_10_00 : {{.*}} {
68+
// CHECK: [[INSTANCE:%[^,]+]] = apply
69+
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
70+
// CHECK: move_value [[LIFETIME]]
71+
// CHECK-LABEL: } // end sil function 'test_10_00'
72+
sil [ossa] @test_10_00 : $@convention(thin) () -> () {
73+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
74+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
75+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
76+
%lifetime = move_value [lexical] %instance : $C
77+
apply %borrow(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
78+
%lifetime2 = move_value %lifetime : $C
79+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
80+
destroy_value %lifetime2 : $C
81+
%retval = tuple ()
82+
return %retval : $()
83+
}
84+
85+
// Old: non-lexical, non-escaping
86+
// New: lexical , non-escaping
87+
// Different. Non-redundant. Keep move_value.
88+
//
89+
// CHECK-LABEL: sil [ossa] @test_00_10 : {{.*}} {
90+
// CHECK: [[INSTANCE:%[^,]+]] = apply
91+
// CHECK: move_value [lexical] [[INSTANCE]]
92+
// CHECK-LABEL: } // end sil function 'test_00_10'
93+
sil [ossa] @test_00_10 : $@convention(thin) () -> () {
94+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
95+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
96+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
97+
apply %borrow(%instance) : $@convention(thin) (@guaranteed C) -> ()
98+
%lifetime2 = move_value [lexical] %instance : $C
99+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
100+
destroy_value %lifetime2 : $C
101+
%retval = tuple ()
102+
return %retval : $()
103+
}
104+
105+
// Old: non-lexical, non-escaping
106+
// New: non-lexical, non-escaping
107+
// Same. Redundant. Remove move_value.
108+
//
109+
// CHECK-LABEL: sil [ossa] @test_00_00 : {{.*}} {
110+
// CHECK: [[INSTANCE:%[^,]+]] = apply
111+
// CHECK-NOT: move_value
112+
// CHECK-LABEL: } // end sil function 'test_00_00'
113+
sil [ossa] @test_00_00 : $@convention(thin) () -> () {
114+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
115+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
116+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
117+
apply %borrow(%instance) : $@convention(thin) (@guaranteed C) -> ()
118+
%lifetime2 = move_value %instance : $C
119+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
120+
destroy_value %lifetime2 : $C
121+
%retval = tuple ()
122+
return %retval : $()
123+
}
124+
125+
// Old: lexical , escaping
126+
// New: lexical , escaping
127+
// Same. Redundant. Remove move_value.
128+
//
129+
// CHECK-LABEL: sil [ossa] @test_11_11 : {{.*}} {
130+
// CHECK: [[INSTANCE:%[^,]+]] = apply
131+
// CHECK: move_value [lexical] [[INSTANCE]]
132+
// CHECK-NOT: move_value
133+
// CHECK-LABEL: } // end sil function 'test_11_11'
134+
sil [ossa] @test_11_11 : $@convention(thin) () -> () {
135+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
136+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
137+
%useUnmanaged = function_ref @useUnmanaged : $@convention(thin) (@sil_unmanaged C) -> ()
138+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
139+
%lifetime = move_value [lexical] %instance : $C
140+
%escape = ref_to_unmanaged %lifetime : $C to $@sil_unmanaged C
141+
apply %useUnmanaged(%escape) : $@convention(thin) (@sil_unmanaged C) -> ()
142+
apply %borrow(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
143+
%lifetime2 = move_value [lexical] %lifetime : $C
144+
%escape2 = ref_to_unmanaged %lifetime2 : $C to $@sil_unmanaged C
145+
apply %useUnmanaged(%escape2) : $@convention(thin) (@sil_unmanaged C) -> ()
146+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
147+
destroy_value %lifetime2 : $C
148+
%retval = tuple ()
149+
return %retval : $()
150+
}
151+
152+
// Old: lexical , escaping
153+
// New: lexical , non-escaping
154+
// Different. Non-redundant. Keep move_value.
155+
//
156+
// CHECK-LABEL: sil [ossa] @test_11_10 : {{.*}} {
157+
// CHECK: [[INSTANCE:%[^,]+]] = apply
158+
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
159+
// CHECK: move_value [lexical] [[LIFETIME]]
160+
// CHECK-LABEL: } // end sil function 'test_11_10'
161+
sil [ossa] @test_11_10 : $@convention(thin) () -> () {
162+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
163+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
164+
%useUnmanaged = function_ref @useUnmanaged : $@convention(thin) (@sil_unmanaged C) -> ()
165+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
166+
%lifetime = move_value [lexical] %instance : $C
167+
%escape = ref_to_unmanaged %lifetime : $C to $@sil_unmanaged C
168+
apply %useUnmanaged(%escape) : $@convention(thin) (@sil_unmanaged C) -> ()
169+
apply %borrow(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
170+
%lifetime2 = move_value [lexical] %lifetime : $C
171+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
172+
destroy_value %lifetime2 : $C
173+
%retval = tuple ()
174+
return %retval : $()
175+
}
176+
177+
// Old: lexical , non-escaping
178+
// New: lexical , escaping
179+
// Different. Non-redundant. Keep move_value.
180+
//
181+
// CHECK-LABEL: sil [ossa] @test_10_11 : {{.*}} {
182+
// CHECK: [[INSTANCE:%[^,]+]] = apply
183+
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
184+
// CHECK: move_value [lexical] [[LIFETIME]]
185+
// CHECK-LABEL: } // end sil function 'test_10_11'
186+
sil [ossa] @test_10_11 : $@convention(thin) () -> () {
187+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
188+
%borrow = function_ref @borrow : $@convention(thin) (@guaranteed C) -> ()
189+
%useUnmanaged = function_ref @useUnmanaged : $@convention(thin) (@sil_unmanaged C) -> ()
190+
%instance = apply %getOwned() : $@convention(thin) () -> (@owned C)
191+
%lifetime = move_value [lexical] %instance : $C
192+
apply %borrow(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
193+
%lifetime2 = move_value [lexical] %lifetime : $C
194+
%escape = ref_to_unmanaged %lifetime2 : $C to $@sil_unmanaged C
195+
apply %useUnmanaged(%escape) : $@convention(thin) (@sil_unmanaged C) -> ()
196+
apply %borrow(%lifetime2) : $@convention(thin) (@guaranteed C) -> ()
197+
destroy_value %lifetime2 : $C
198+
%retval = tuple ()
199+
return %retval : $()
200+
}

0 commit comments

Comments
 (0)