11
11
// ===----------------------------------------------------------------------===//
12
12
13
13
#define DEBUG_TYPE " sil-semantic-arc-opts"
14
+ #include " swift/Basic/STLExtras.h"
14
15
#include " swift/SIL/BasicBlockUtils.h"
15
16
#include " swift/SIL/OwnershipUtils.h"
16
17
#include " swift/SIL/SILArgument.h"
17
18
#include " swift/SIL/SILInstruction.h"
19
+ #include " swift/SIL/SILVisitor.h"
18
20
#include " swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
19
21
#include " swift/SILOptimizer/PassManager/Passes.h"
20
22
#include " swift/SILOptimizer/PassManager/Transforms.h"
@@ -26,123 +28,144 @@ using namespace swift;
26
28
27
29
STATISTIC (NumEliminatedInsts, " number of removed instructions" );
28
30
29
- static bool optimizeGuaranteedArgument (SILArgument *Arg,
30
- OwnershipChecker &Checker) {
31
- bool MadeChange = false ;
31
+ namespace {
32
32
33
- // Gather all copy_value users of Arg.
34
- llvm::SmallVector<CopyValueInst *, 4 > Worklist;
35
- for (auto *Op : Arg->getUses ()) {
36
- if (auto *CVI = dyn_cast<CopyValueInst>(Op->getUser ())) {
37
- Worklist.push_back (CVI);
38
- }
39
- }
33
+ struct SemanticARCOptVisitor
34
+ : SILInstructionVisitor<SemanticARCOptVisitor, bool > {
35
+ bool visitSILInstruction (SILInstruction *i) { return false ; }
36
+ bool visitCopyValueInst (CopyValueInst *cvi);
37
+ bool visitBeginBorrowInst (BeginBorrowInst *bbi);
38
+ };
39
+
40
+ } // end anonymous namespace
40
41
41
- // Then until we run out of copies...
42
- while (!Worklist.empty ()) {
43
- auto *CVI = Worklist.pop_back_val ();
44
-
45
- // Quickly see if copy has only one use and that use is a destroy_value. In
46
- // such a case, we can always eliminate both the copy and the destroy.
47
- if (auto *Op = CVI->getSingleUse ()) {
48
- if (auto *DVI = dyn_cast<DestroyValueInst>(Op->getUser ())) {
49
- DVI->eraseFromParent ();
50
- CVI->eraseFromParent ();
51
- NumEliminatedInsts += 2 ;
42
+ bool SemanticARCOptVisitor::visitBeginBorrowInst (BeginBorrowInst *bbi) {
43
+ auto kind = bbi->getOperand ().getOwnershipKind ();
44
+ SmallVector<EndBorrowInst *, 16 > endBorrows;
45
+ for (auto *op : bbi->getUses ()) {
46
+ auto *user = op->getUser ();
47
+ switch (user->getKind ()) {
48
+ case SILInstructionKind::EndBorrowInst:
49
+ endBorrows.push_back (cast<EndBorrowInst>(user));
50
+ break ;
51
+ default :
52
+ // Make sure that this operand can accept our arguments kind.
53
+ auto map = op->getOwnershipKindMap ();
54
+ if (map.canAcceptKind (kind))
52
55
continue ;
53
- }
56
+ return false ;
54
57
}
58
+ }
55
59
56
- // Ok, now run the checker on the copy value. If it fails, then we just
57
- // continue.
58
- if (!Checker.checkValue (CVI))
59
- continue ;
60
-
61
- // Otherwise, lets do a quick check on what the checker thinks the lifetime
62
- // ending and non-lifetime ending users. To be conservative, we bail unless
63
- // each lifetime ending use is a destroy_value and if each non-lifetime
64
- // ending use is one of the following instructions:
65
- //
66
- // 1. copy_value.
67
- // 2. begin_borrow.
68
- // 3. end_borrow.
69
- if (!all_of (Checker.lifetimeEndingUsers , [](SILInstruction *I) -> bool {
70
- return isa<DestroyValueInst>(I);
71
- }))
72
- continue ;
73
-
74
- // Extra copy values that we should visit recursively.
75
- llvm::SmallVector<CopyValueInst *, 8 > NewCopyInsts;
76
- llvm::SmallVector<SILInstruction *, 8 > NewBorrowInsts;
77
- if (!all_of (Checker.regularUsers , [&](SILInstruction *I) -> bool {
78
- if (auto *CVI = dyn_cast<CopyValueInst>(I)) {
79
- NewCopyInsts.push_back (CVI);
80
- return true ;
81
- }
82
-
83
- if (!isa<BeginBorrowInst>(I) && !isa<EndBorrowInst>(I))
84
- return false ;
85
-
86
- NewBorrowInsts.push_back (I);
87
- return true ;
88
- }))
89
- continue ;
90
-
91
- // Ok! we can remove the copy_value, destroy_values!
92
- MadeChange = true ;
93
- CVI->replaceAllUsesWith (CVI->getOperand ());
94
- CVI->eraseFromParent ();
60
+ // At this point, we know that the begin_borrow's operand can be
61
+ // used as an argument to all non-end borrow uses. Eliminate the
62
+ // begin borrow and end borrows.
63
+ while (!endBorrows.empty ()) {
64
+ auto *ebi = endBorrows.pop_back_val ();
65
+ ebi->eraseFromParent ();
95
66
++NumEliminatedInsts;
67
+ }
68
+ bbi->replaceAllUsesWith (bbi->getOperand ());
69
+ bbi->eraseFromParent ();
70
+ ++NumEliminatedInsts;
71
+ return true ;
72
+ }
96
73
97
- while (!Checker.lifetimeEndingUsers .empty ()) {
98
- Checker.lifetimeEndingUsers .pop_back_val ()->eraseFromParent ();
99
- ++NumEliminatedInsts;
100
- }
101
-
102
- // Then add the copy_values that were users of our original copy value to
103
- // the worklist.
104
- while (!NewCopyInsts.empty ()) {
105
- Worklist.push_back (NewCopyInsts.pop_back_val ());
106
- }
107
-
108
- // Then remove any begin/end borrow that we found. These are unneeded since
109
- // the lifetime guarantee from the argument exists above and beyond said
110
- // scope.
111
- while (!NewBorrowInsts.empty ()) {
112
- SILInstruction *I = NewBorrowInsts.pop_back_val ();
113
- if (auto *BBI = dyn_cast<BeginBorrowInst>(I)) {
114
- // Any copy_value that is used by the begin borrow is added to the
115
- // worklist.
116
- for (auto *BBIUse : BBI->getUses ()) {
117
- if (auto *BBIUseCopyValue =
118
- dyn_cast<CopyValueInst>(BBIUse->getUser ())) {
119
- Worklist.push_back (BBIUseCopyValue);
120
- }
121
- }
74
+ static bool canHandleOperand (SILValue operand, SmallVectorImpl<SILValue> &out) {
75
+ if (!getUnderlyingBorrowIntroducers (operand, out))
76
+ return false ;
122
77
123
- // First go through and eliminate all end borrows.
124
- SmallVector<EndBorrowInst *, 4 > endBorrows;
125
- copy (BBI->getEndBorrows (), std::back_inserter (endBorrows));
126
- while (!endBorrows.empty ()) {
127
- endBorrows.pop_back_val ()->eraseFromParent ();
128
- ++NumEliminatedInsts;
129
- }
78
+ // / TODO: Add support for begin_borrow, load_borrow.
79
+ return all_of (out, [](SILValue v) { return isa<SILFunctionArgument>(v); });
80
+ }
130
81
131
- // Then eliminate BBI itself.
132
- BBI->replaceAllUsesWith (BBI->getOperand ());
133
- BBI->eraseFromParent ();
134
- ++NumEliminatedInsts;
82
+ static bool performGuaranteedCopyValueOptimization (CopyValueInst *cvi) {
83
+ SmallVector<SILValue, 16 > borrowIntroducers;
84
+
85
+ // Whitelist the operands that we know how to support and make sure
86
+ // our operand is actually guaranteed.
87
+ if (!canHandleOperand (cvi->getOperand (), borrowIntroducers))
88
+ return false ;
89
+
90
+ // Then go over all of our uses. Find our destroying instructions
91
+ // and make sure all of them are destroy_value. For our
92
+ // non-destroying instructions, make sure that they accept a
93
+ // guaranteed value. After that, make sure that our destroys are
94
+ // within the lifetime of our borrowed values.
95
+ SmallVector<DestroyValueInst *, 16 > destroys;
96
+ for (auto *op : cvi->getUses ()) {
97
+ // We know that a copy_value produces an @owned value. Look
98
+ // through all of our uses and classify them as either
99
+ // invalidating or not invalidating. Make sure that all of the
100
+ // invalidating ones are destroy_value since otherwise the
101
+ // live_range is not complete.
102
+ auto map = op->getOwnershipKindMap ();
103
+ auto constraint = map.getLifetimeConstraint (ValueOwnershipKind::Owned);
104
+ switch (constraint) {
105
+ case UseLifetimeConstraint::MustBeInvalidated:
106
+ // And we have a destroy_value, track it and continue.
107
+ if (auto *dvi = dyn_cast<DestroyValueInst>(op->getUser ())) {
108
+ destroys.push_back (dvi);
135
109
continue ;
136
110
}
111
+ // Otherwise, we found a non-destroy value invalidating owned
112
+ // user... This is not an unnecessary live range.
113
+ return false ;
114
+ case UseLifetimeConstraint::MustBeLive:
115
+ // Ok, this constraint can take something owned as live. Lets
116
+ // see if it can also take something that is guaranteed. If it
117
+ // can not, then we bail.
118
+ if (!map.canAcceptKind (ValueOwnershipKind::Guaranteed)) {
119
+ return false ;
120
+ }
137
121
138
- // This is not necessary, but it does add a check.
139
- auto *EBI = cast<EndBorrowInst>(I);
140
- EBI->eraseFromParent ();
141
- ++NumEliminatedInsts;
122
+ // Otherwise, continue.
123
+ continue ;
142
124
}
143
125
}
144
126
145
- return MadeChange;
127
+ // If we reached this point, then we know that all of our users can
128
+ // accept a guaranteed value and our owned value is destroyed only
129
+ // by destroy_value. Check if all of our destroys are joint
130
+ // post-dominated by the end_borrow set. If they do not, then the
131
+ // copy_value is lifetime extending the guaranteed value, we can not
132
+ // eliminate it.
133
+ //
134
+ // TODO: When we support begin_borrow/load_borrow a linear linfetime
135
+ // check will be needed here.
136
+ assert (all_of (borrowIntroducers,
137
+ [](SILValue v) { return isa<SILFunctionArgument>(v); }));
138
+
139
+ // Otherwise, we know that our copy_value/destroy_values are all
140
+ // completely within the guaranteed value scope.
141
+ while (!destroys.empty ()) {
142
+ auto *dvi = destroys.pop_back_val ();
143
+ dvi->eraseFromParent ();
144
+ ++NumEliminatedInsts;
145
+ }
146
+ cvi->replaceAllUsesWith (cvi->getOperand ());
147
+ cvi->eraseFromParent ();
148
+ ++NumEliminatedInsts;
149
+ return true ;
150
+ }
151
+
152
+ bool SemanticARCOptVisitor::visitCopyValueInst (CopyValueInst *cvi) {
153
+ // If our copy value inst has a single destroy value user, eliminate
154
+ // it.
155
+ if (auto *op = cvi->getSingleUse ()) {
156
+ if (auto *dvi = dyn_cast<DestroyValueInst>(op->getUser ())) {
157
+ dvi->eraseFromParent ();
158
+ cvi->eraseFromParent ();
159
+ NumEliminatedInsts += 2 ;
160
+ return true ;
161
+ }
162
+ }
163
+
164
+ // Then try to perform the guaranteed copy value optimization.
165
+ if (performGuaranteedCopyValueOptimization (cvi))
166
+ return true ;
167
+
168
+ return false ;
146
169
}
147
170
148
171
// ===----------------------------------------------------------------------===//
@@ -156,31 +179,56 @@ namespace {
156
179
// configuration.
157
180
struct SemanticARCOpts : SILFunctionTransform {
158
181
void run () override {
159
- bool MadeChange = false ;
160
- SILFunction *F = getFunction ();
161
- if (!F->getModule ().isStdlibModule ()) {
182
+ SILFunction &f = *getFunction ();
183
+ // Do not run the semantic arc opts unless we are running on the
184
+ // standard library. This is because this is the only module that
185
+ // passes the ownership verifier.
186
+ if (!f.getModule ().isStdlibModule ())
162
187
return ;
163
- }
164
188
165
- DeadEndBlocks DEBlocks (F);
166
- OwnershipChecker Checker{{}, {}, {}, {}, F->getModule (), DEBlocks};
167
-
168
- // First as a special case, handle guaranteed SIL function arguments.
189
+ // Iterate over all of the arguments, performing small peephole
190
+ // ARC optimizations.
169
191
//
170
- // The reason that this is special is that we do not need to consider the
171
- // end of the borrow scope since the end of the function is the end of the
172
- // borrow scope.
173
- for (auto *Arg : F->getArguments ()) {
174
- if (Arg->getOwnershipKind () != ValueOwnershipKind::Guaranteed)
192
+ // FIXME: Should we iterate or use a RPOT order here?
193
+ bool madeChange = false ;
194
+ for (auto &bb : f) {
195
+ auto ii = bb.rend ();
196
+ auto start = bb.rbegin ();
197
+
198
+ // If the bb is empty, continue.
199
+ if (start == ii)
175
200
continue ;
176
- MadeChange |= optimizeGuaranteedArgument (Arg, Checker);
201
+
202
+ // Go to the first instruction to process.
203
+ --ii;
204
+
205
+ // Then until we process the first instruction of the block...
206
+ while (ii != start) {
207
+ // Move the iterator before ii.
208
+ auto tmp = std::next (ii);
209
+
210
+ // Then try to optimize. If we succeeded, then we deleted
211
+ // ii. Move ii from the next value back onto the instruction
212
+ // after ii's old value in the block instruction list and then
213
+ // process that.
214
+ if (SemanticARCOptVisitor ().visit (&*ii)) {
215
+ madeChange = true ;
216
+ ii = std::prev (tmp);
217
+ continue ;
218
+ }
219
+
220
+ // Otherwise, we didn't delete ii. Just visit the next instruction.
221
+ --ii;
222
+ }
223
+
224
+ // Finally visit the first instruction of the block.
225
+ madeChange |= SemanticARCOptVisitor ().visit (&*ii);
177
226
}
178
227
179
- if (MadeChange ) {
228
+ if (madeChange ) {
180
229
invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
181
230
}
182
231
}
183
-
184
232
};
185
233
186
234
} // end anonymous namespace
0 commit comments