87
87
88
88
#include " PhiStorageOptimizer.h"
89
89
#include " swift/SIL/BasicBlockDatastructures.h"
90
+ #include " swift/SIL/Dominance.h"
91
+ #include " swift/SIL/NodeDatastructures.h"
90
92
#include " swift/SIL/SILBasicBlock.h"
91
93
#include " swift/SIL/SILInstruction.h"
92
94
@@ -101,35 +103,46 @@ namespace swift {
101
103
class PhiStorageOptimizer {
102
104
PhiValue phi;
103
105
const ValueStorageMap &valueStorageMap;
106
+ DominanceInfo *domInfo;
104
107
105
108
CoalescedPhi &coalescedPhi;
106
109
107
110
BasicBlockSet occupiedBlocks;
108
111
109
112
public:
110
113
PhiStorageOptimizer (PhiValue phi, const ValueStorageMap &valueStorageMap,
111
- CoalescedPhi &coalescedPhi)
112
- : phi(phi), valueStorageMap(valueStorageMap), coalescedPhi(coalescedPhi ),
113
- occupiedBlocks (getFunction()) {}
114
+ CoalescedPhi &coalescedPhi, DominanceInfo *domInfo )
115
+ : phi(phi), valueStorageMap(valueStorageMap), domInfo(domInfo ),
116
+ coalescedPhi (coalescedPhi), occupiedBlocks(getFunction()) {}
114
117
115
118
SILFunction *getFunction () const { return phi.phiBlock ->getParent (); }
116
119
117
120
void optimize ();
118
121
119
122
protected:
120
- bool hasUseProjection (SILInstruction *defInst);
121
- bool canCoalesceValue (SILValue incomingVal);
123
+ using ProjectedValues = StackList<SILValue>;
124
+ bool findUseProjections (SILInstruction *defInst,
125
+ SmallVectorImpl<Operand *> &operands);
126
+ bool findDefProjections (SILValue value,
127
+ SmallVectorImpl<SILValue> *projecteds);
128
+ SILBasicBlock *canCoalesceValue (SILValue incomingVal,
129
+ ProjectedValues *projectedValues);
122
130
void tryCoalesceOperand (SILBasicBlock *incomingPred);
123
- bool recordUseLiveness (SILValue incomingVal, BasicBlockSetVector &liveBlocks);
131
+ bool recordUseLiveness (ProjectedValues &incomingVals,
132
+ SILBasicBlock *dominatingBlock,
133
+ BasicBlockSetVector &liveBlocks);
134
+ SILBasicBlock *getDominatingBlock (SILValue incomingVal,
135
+ StackList<SILValue> *projectedValues);
124
136
};
125
137
126
138
} // namespace swift
127
139
128
140
void CoalescedPhi::coalesce (PhiValue phi,
129
- const ValueStorageMap &valueStorageMap) {
141
+ const ValueStorageMap &valueStorageMap,
142
+ DominanceInfo *domInfo) {
130
143
assert (empty () && " attempt to recoalesce the same phi" );
131
144
132
- PhiStorageOptimizer (phi, valueStorageMap, *this ).optimize ();
145
+ PhiStorageOptimizer (phi, valueStorageMap, *this , domInfo ).optimize ();
133
146
}
134
147
135
148
// / Optimize phi storage by coalescing phi operands.
@@ -168,7 +181,8 @@ void CoalescedPhi::coalesce(PhiValue phi,
168
181
void PhiStorageOptimizer::optimize () {
169
182
// The single incoming value case always projects storage.
170
183
if (auto *predecessor = phi.phiBlock ->getSinglePredecessorBlock ()) {
171
- if (canCoalesceValue (phi.getValue ()->getIncomingPhiValue (predecessor))) {
184
+ if (canCoalesceValue (phi.getValue ()->getIncomingPhiValue (predecessor),
185
+ /* projectedValues=*/ nullptr )) {
172
186
// Storage will always be allocated for the phi. The optimization
173
187
// attempts to let incoming values reuse the phi's storage. This isn't
174
188
// always possible, even in the single incoming value case. For example,
@@ -183,21 +197,47 @@ void PhiStorageOptimizer::optimize() {
183
197
}
184
198
}
185
199
186
- // Return true if any of \p defInst's operands are composing use projections
187
- // into \p defInst's storage.
188
- bool PhiStorageOptimizer::hasUseProjection (SILInstruction *defInst) {
200
+ // Return \p defInst's operand which is a composing use projection into \p
201
+ // defInst's storage, or nullptr if none exists.
202
+ bool PhiStorageOptimizer::findUseProjections (
203
+ SILInstruction *defInst, SmallVectorImpl<Operand *> &operands) {
204
+ bool found = false ;
189
205
for (Operand &oper : defInst->getAllOperands ()) {
190
- if (valueStorageMap.isComposingUseProjection (&oper))
191
- return true ;
206
+ if (valueStorageMap.isComposingUseProjection (&oper)) {
207
+ found = true ;
208
+ operands.push_back (&oper);
209
+ }
210
+ }
211
+ return found;
212
+ }
213
+
214
+ bool PhiStorageOptimizer::findDefProjections (
215
+ SILValue value, SmallVectorImpl<SILValue> *projecteds) {
216
+ bool found = false ;
217
+ for (auto user : value->getUsers ()) {
218
+ for (auto result : user->getResults ()) {
219
+ auto *storage = valueStorageMap.getStorageOrNull (result);
220
+ if (!storage)
221
+ continue ;
222
+ if (storage->isDefProjection ) {
223
+ assert (storage->projectedStorageID ==
224
+ valueStorageMap.getOrdinal (value));
225
+ projecteds->push_back (result);
226
+ found = true ;
227
+ }
228
+ }
192
229
}
193
- return false ;
230
+ return found ;
194
231
}
195
232
196
- // Return true in \p incomingVal can be coalesced with this phi ignoring
197
- // possible interference. Simply determine whether storage reuse is possible.
233
+ // Determine whether storage reuse is possible with \p incomingVal. Ignore
234
+ // possible interference. If reuse is possible, return the block which
235
+ // dominates all defs whose storage is projected out of incomingVal.
198
236
//
199
237
// Precondition: \p incomingVal is an operand of this phi.
200
- bool PhiStorageOptimizer::canCoalesceValue (SILValue incomingVal) {
238
+ SILBasicBlock *
239
+ PhiStorageOptimizer::canCoalesceValue (SILValue incomingVal,
240
+ ProjectedValues *projectedValues) {
201
241
// A Phi must not project from storage that was initialized on a path that
202
242
// reaches the phi because other uses of the storage may interfere with the
203
243
// phi. A phi may, however, be a composing use projection.
@@ -215,31 +255,20 @@ bool PhiStorageOptimizer::canCoalesceValue(SILValue incomingVal) {
215
255
// could be handled, but would require by recursively following uses across
216
256
// projections when computing liveness.
217
257
if (!incomingStorage.isAllocated () || incomingStorage.isProjection ())
218
- return false ;
258
+ return nullptr ;
219
259
220
- auto *defInst = incomingVal->getDefiningInstruction ();
221
- if (!defInst) {
222
- // Indirect function arguments were replaced by loads.
223
- assert (!isa<SILFunctionArgument>(incomingVal));
260
+ if (PhiValue (incomingVal)) {
224
261
// Do not coalesce a phi with other phis. This would require liveness
225
262
// analysis of the whole phi web before coalescing phi operands.
226
- return false ;
263
+ return nullptr ;
227
264
}
228
265
229
266
// Don't coalesce an incoming value unless it's storage is from a stack
230
267
// allocation, which can be replaced with another alloc_stack.
231
268
if (!isa<AllocStackInst>(incomingStorage.storageAddress ))
232
- return false ;
269
+ return nullptr ;
233
270
234
- // Make sure that the incomingVal is not coalesced with any of its operands.
235
- //
236
- // Handling incomingValues whose operands project into them would require by
237
- // recursively finding the set of value definitions and their dominating defBB
238
- // instead of simply incomingVal->getParentBlock().
239
- if (hasUseProjection (defInst))
240
- return false ;
241
-
242
- return true ;
271
+ return getDominatingBlock (incomingVal, projectedValues);
243
272
}
244
273
245
274
// Process a single incoming phi operand. Compute the value's liveness while
@@ -248,11 +277,13 @@ void PhiStorageOptimizer::tryCoalesceOperand(SILBasicBlock *incomingPred) {
248
277
Operand *incomingOper = phi.getOperand (incomingPred);
249
278
SILValue incomingVal = incomingOper->get ();
250
279
251
- if (!canCoalesceValue (incomingVal))
280
+ ProjectedValues projectedValues (incomingPred->getFunction ());
281
+ auto *dominatingBlock = canCoalesceValue (incomingVal, &projectedValues);
282
+ if (!dominatingBlock)
252
283
return ;
253
284
254
285
BasicBlockSetVector liveBlocks (getFunction ());
255
- if (!recordUseLiveness (incomingVal , liveBlocks))
286
+ if (!recordUseLiveness (projectedValues, dominatingBlock , liveBlocks))
256
287
return ;
257
288
258
289
for (auto *block : liveBlocks) {
@@ -262,39 +293,100 @@ void PhiStorageOptimizer::tryCoalesceOperand(SILBasicBlock *incomingPred) {
262
293
coalescedPhi.coalescedOperands .push_back (incomingOper);
263
294
}
264
295
265
- // Record liveness generated by uses of \p incomingVal.
296
+ // / The block which dominates all the defs whose storage is projected out of the
297
+ // / storage for \p incomingVal.
298
+ // /
299
+ // / Populates \p projectedValues with the values whose storage is recursively
300
+ // / projected out of the storage for incomingVal.
301
+ SILBasicBlock *
302
+ PhiStorageOptimizer::getDominatingBlock (SILValue incomingVal,
303
+ StackList<SILValue> *projectedValues) {
304
+ assert (domInfo);
305
+
306
+ // Recursively find the set of value definitions and the dominating LCA.
307
+ ValueWorklist values (incomingVal->getFunction ());
308
+
309
+ values.push (incomingVal);
310
+
311
+ SILBasicBlock *lca = incomingVal->getParentBlock ();
312
+ auto updateLCA = [&](SILBasicBlock *other) {
313
+ lca = domInfo->findNearestCommonDominator (lca, other);
314
+ };
315
+
316
+ while (auto value = values.pop ()) {
317
+ if (projectedValues)
318
+ projectedValues->push_back (value);
319
+ auto *defInst = value->getDefiningInstruction ();
320
+ if (!defInst) {
321
+ assert (!PhiValue (value));
322
+ updateLCA (value->getParentBlock ());
323
+ continue ;
324
+ }
325
+ SmallVector<Operand *, 2 > operands;
326
+ if (findUseProjections (defInst, operands)) {
327
+ // Any operand whose storage is a use-projection out of `value`'s storage
328
+ // dominates `value` (i.e. `defInst`), so skip updating the LCA here.
329
+ for (auto *operand : operands) {
330
+ values.pushIfNotVisited (operand->get ());
331
+ }
332
+ continue ;
333
+ }
334
+ SmallVector<SILValue, 2 > projecteds;
335
+ if (findDefProjections (value, &projecteds)) {
336
+ // Walking up the projection hain from this point, every subsequent
337
+ // projection must be a def projection [projection_chain_structure].
338
+ // Every such projection is dominated by `value` (i.e. defInst).
339
+ //
340
+ // If the walk were only updating the LCA, it could stop here.
341
+ //
342
+ // It is also collecting values whose storage is projected out of the
343
+ // phi, however, so the walk must continue.
344
+ updateLCA (defInst->getParent ());
345
+ for (auto projected : projecteds) {
346
+ values.pushIfNotVisited (projected);
347
+ }
348
+ continue ;
349
+ }
350
+ updateLCA (defInst->getParent ());
351
+ }
352
+ return lca;
353
+ }
354
+
355
+ // Record liveness generated by uses of \p projectedVals.
266
356
//
267
357
// Return true if no interference was detected along the way.
268
- bool PhiStorageOptimizer::recordUseLiveness (SILValue incomingVal,
358
+ bool PhiStorageOptimizer::recordUseLiveness (ProjectedValues &projectedVals,
359
+ SILBasicBlock *dominatingBlock,
269
360
BasicBlockSetVector &liveBlocks) {
270
361
assert (liveBlocks.empty ());
271
362
272
- // Stop liveness traversal at defBB.
273
- SILBasicBlock *defBB = incomingVal->getParentBlock ();
274
- for (auto *use : incomingVal->getUses ()) {
275
- StackList<SILBasicBlock *> liveBBWorklist (getFunction ());
363
+ for (auto projectedVal : projectedVals) {
364
+ for (auto *use : projectedVal->getUses ()) {
365
+ StackList<SILBasicBlock *> liveBBWorklist (getFunction ());
366
+
367
+ // If \p liveBB is already occupied by another value, return
368
+ // false. Otherwise, mark \p liveBB live and push it onto liveBBWorklist.
369
+ auto visitLiveBlock = [&](SILBasicBlock *liveBB) {
370
+ assert (liveBB != phi.phiBlock && " phi operands are consumed" );
276
371
277
- // If \p liveBB is already occupied by another value, return
278
- // false. Otherwise, mark \p liveBB live and push it onto liveBBWorklist.
279
- auto visitLiveBlock = [&](SILBasicBlock *liveBB) {
280
- assert (liveBB != phi.phiBlock && " phi operands are consumed" );
372
+ if (occupiedBlocks.contains (liveBB))
373
+ return false ;
281
374
282
- if (occupiedBlocks.contains (liveBB))
375
+ // Stop liveness traversal at dominatingBlock.
376
+ if (liveBlocks.insert (liveBB) && liveBB != dominatingBlock) {
377
+ liveBBWorklist.push_back (liveBB);
378
+ }
379
+ return true ;
380
+ };
381
+ if (!visitLiveBlock (use->getUser ()->getParent ()))
283
382
return false ;
284
383
285
- if (liveBlocks.insert (liveBB) && liveBB != defBB) {
286
- liveBBWorklist.push_back (liveBB);
287
- }
288
- return true ;
289
- };
290
- if (!visitLiveBlock (use->getUser ()->getParent ()))
291
- return false ;
292
-
293
- while (!liveBBWorklist.empty ()) {
294
- auto *succBB = liveBBWorklist.pop_back_val ();
295
- for (auto *predBB : succBB->getPredecessorBlocks ()) {
296
- if (!visitLiveBlock (predBB))
297
- return false ;
384
+ while (!liveBBWorklist.empty ()) {
385
+ auto *succBB = liveBBWorklist.pop_back_val ();
386
+ for (auto *predBB : succBB->getPredecessorBlocks ()) {
387
+ if (!visitLiveBlock (predBB))
388
+ return false ;
389
+ }
298
390
}
299
391
}
300
392
}
0 commit comments