15
15
#include " swift/SIL/OwnershipUtils.h"
16
16
#include " swift/SIL/SILArgument.h"
17
17
#include " swift/SIL/SILInstruction.h"
18
+ #include " swift/SIL/SILVisitor.h"
19
+ #include " swift/Basic/STLExtras.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,143 @@ 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
+ // / TODO: Add support for begin_borrow, load_borrow.
75
+ static bool canHandleOperand (SILValue operand) {
76
+ if (auto *arg = dyn_cast<SILFunctionArgument>(operand)) {
77
+ return arg->getOwnershipKind () == ValueOwnershipKind::Guaranteed;
78
+ }
122
79
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
- }
80
+ return false ;
81
+ }
130
82
131
- // Then eliminate BBI itself.
132
- BBI->replaceAllUsesWith (BBI->getOperand ());
133
- BBI->eraseFromParent ();
134
- ++NumEliminatedInsts;
83
+ static bool performGuaranteedCopyValueOptimization (CopyValueInst *cvi) {
84
+ // Whitelist the operands that we know how to support and make sure
85
+ // our operand is actually guaranteed.
86
+ if (!canHandleOperand (cvi->getOperand ()))
87
+ return false ;
88
+
89
+ // Then go over all of our uses. Find our destroying instructions
90
+ // and make sure all of them are destroy_value. For our
91
+ // non-destroying instructions, make sure that they accept a
92
+ // guaranteed value. After that, make sure that our destroys are
93
+ // within the lifetime of our borrowed values.
94
+ SmallVector<DestroyValueInst *, 16 > destroys;
95
+ for (auto *op : cvi->getUses ()) {
96
+ // We know that a copy_value produces an @owned value. Look
97
+ // through all of our uses and classify them as either
98
+ // invalidating or not invalidating. Make sure that all of the
99
+ // invalidating ones are destroy_value since otherwise the
100
+ // live_range is not complete.
101
+ auto map = op->getOwnershipKindMap ();
102
+ auto constraint = map.getLifetimeConstraint (ValueOwnershipKind::Owned);
103
+ switch (constraint) {
104
+ case UseLifetimeConstraint::MustBeInvalidated:
105
+ // And we have a destroy_value, track it and continue.
106
+ if (auto *dvi = dyn_cast<DestroyValueInst>(op->getUser ())) {
107
+ destroys.push_back (dvi);
135
108
continue ;
136
109
}
110
+ // Otherwise, we found a non-destroy value invalidating owned
111
+ // user... This is not an unnecessary live range.
112
+ return false ;
113
+ case UseLifetimeConstraint::MustBeLive:
114
+ // Ok, this constraint can take something owned as live. Lets
115
+ // see if it can also take something that is guaranteed. If it
116
+ // can not, then we bail.
117
+ if (!map.canAcceptKind (ValueOwnershipKind::Guaranteed)) {
118
+ return false ;
119
+ }
137
120
138
- // This is not necessary, but it does add a check.
139
- auto *EBI = cast<EndBorrowInst>(I);
140
- EBI->eraseFromParent ();
141
- ++NumEliminatedInsts;
121
+ // Otherwise, continue.
122
+ continue ;
142
123
}
143
124
}
144
125
145
- return MadeChange;
126
+ // If we reached this point, then we know that all of our users can
127
+ // accept a guaranteed value and our owned value is destroyed only
128
+ // by destroy_value. Check if all of our destroys are joint
129
+ // post-dominated by the end_borrow set. If they do not, then the
130
+ // copy_value is lifetime extending the guaranteed value, we can not
131
+ // eliminate it.
132
+ //
133
+ // TODO: When we support begin_borrow/load_borrow a linear linfetime
134
+ // check will be needed here.
135
+ assert (isa<SILFunctionArgument>(cvi->getOperand ()) &&
136
+ " This code must be updated for begin_borrow/load_borrow?!" );
137
+
138
+ // Otherwise, we know that our copy_value/destroy_values are all
139
+ // completely within the guaranteed value scope.
140
+ while (!destroys.empty ()) {
141
+ auto *dvi = destroys.pop_back_val ();
142
+ dvi->eraseFromParent ();
143
+ ++NumEliminatedInsts;
144
+ }
145
+ cvi->replaceAllUsesWith (cvi->getOperand ());
146
+ cvi->eraseFromParent ();
147
+ ++NumEliminatedInsts;
148
+ return true ;
149
+ }
150
+
151
+ bool SemanticARCOptVisitor::visitCopyValueInst (CopyValueInst *cvi) {
152
+ // If our copy value inst has a single destroy value user, eliminate
153
+ // it.
154
+ if (auto *op = cvi->getSingleUse ()) {
155
+ if (auto *dvi = dyn_cast<DestroyValueInst>(op->getUser ())) {
156
+ dvi->eraseFromParent ();
157
+ cvi->eraseFromParent ();
158
+ NumEliminatedInsts += 2 ;
159
+ return true ;
160
+ }
161
+ }
162
+
163
+ // Then try to perform the guaranteed copy value optimization.
164
+ if (performGuaranteedCopyValueOptimization (cvi))
165
+ return true ;
166
+
167
+ return false ;
146
168
}
147
169
148
170
// ===----------------------------------------------------------------------===//
@@ -156,31 +178,56 @@ namespace {
156
178
// configuration.
157
179
struct SemanticARCOpts : SILFunctionTransform {
158
180
void run () override {
159
- bool MadeChange = false ;
160
- SILFunction *F = getFunction ();
161
- if (!F->getModule ().isStdlibModule ()) {
181
+ SILFunction &f = *getFunction ();
182
+ // Do not run the semantic arc opts unless we are running on the
183
+ // standard library. This is because this is the only module that
184
+ // passes the ownership verifier.
185
+ if (!f.getModule ().isStdlibModule ())
162
186
return ;
163
- }
164
187
165
- DeadEndBlocks DEBlocks (F);
166
- OwnershipChecker Checker{{}, {}, {}, {}, F->getModule (), DEBlocks};
167
-
168
- // First as a special case, handle guaranteed SIL function arguments.
188
+ // Iterate over all of the arguments, performing small peephole
189
+ // ARC optimizations.
169
190
//
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)
191
+ // FIXME: Should we iterate or use a RPOT order here?
192
+ bool madeChange = false ;
193
+ for (auto &bb : f) {
194
+ auto ii = bb.rend ();
195
+ auto start = bb.rbegin ();
196
+
197
+ // If the bb is empty, continue.
198
+ if (start == ii)
175
199
continue ;
176
- MadeChange |= optimizeGuaranteedArgument (Arg, Checker);
200
+
201
+ // Go to the first instruction to process.
202
+ --ii;
203
+
204
+ // Then until we process the first instruction of the block...
205
+ while (ii != start) {
206
+ // Move the iterator before ii.
207
+ auto tmp = std::next (ii);
208
+
209
+ // Then try to optimize. If we succeeded, then we deleted
210
+ // ii. Move ii from the next value back onto the instruction
211
+ // after ii's old value in the block instruction list and then
212
+ // process that.
213
+ if (SemanticARCOptVisitor ().visit (&*ii)) {
214
+ madeChange = true ;
215
+ ii = std::prev (tmp);
216
+ continue ;
217
+ }
218
+
219
+ // Otherwise, we didn't delete ii. Just visit the next instruction.
220
+ --ii;
221
+ }
222
+
223
+ // Finally visit the first instruction of the block.
224
+ madeChange |= SemanticARCOptVisitor ().visit (&*ii);
177
225
}
178
226
179
- if (MadeChange ) {
227
+ if (madeChange ) {
180
228
invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
181
229
}
182
230
}
183
-
184
231
};
185
232
186
233
} // end anonymous namespace
0 commit comments