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
+ ProjectedValues *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,53 @@ 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
+ auto *svi = dyn_cast<SingleValueInstruction>(defInst);
205
+ if (!svi) {
206
+ return false ;
207
+ }
208
+ auto ordinal = valueStorageMap.getOrdinal (svi);
209
+ bool found = false ;
189
210
for (Operand &oper : defInst->getAllOperands ()) {
190
- if (valueStorageMap.isComposingUseProjection (&oper))
191
- return true ;
211
+ if (valueStorageMap.isComposingUseProjection (&oper) &&
212
+ valueStorageMap.getStorage (oper.get ()).projectedStorageID == ordinal) {
213
+ found = true ;
214
+ operands.push_back (&oper);
215
+ }
216
+ }
217
+ return found;
218
+ }
219
+
220
+ bool PhiStorageOptimizer::findDefProjections (
221
+ SILValue value, SmallVectorImpl<SILValue> *projecteds) {
222
+ bool found = false ;
223
+ for (auto user : value->getUsers ()) {
224
+ for (auto result : user->getResults ()) {
225
+ auto *storage = valueStorageMap.getStorageOrNull (result);
226
+ if (!storage)
227
+ continue ;
228
+ if (storage->isDefProjection ) {
229
+ assert (storage->projectedStorageID ==
230
+ valueStorageMap.getOrdinal (value));
231
+ projecteds->push_back (result);
232
+ found = true ;
233
+ }
234
+ }
192
235
}
193
- return false ;
236
+ return found ;
194
237
}
195
238
196
- // Return true in \p incomingVal can be coalesced with this phi ignoring
197
- // possible interference. Simply determine whether storage reuse is possible.
239
+ // Determine whether storage reuse is possible with \p incomingVal. Ignore
240
+ // possible interference. If reuse is possible, return the block which
241
+ // dominates all defs whose storage is projected out of incomingVal.
198
242
//
199
243
// Precondition: \p incomingVal is an operand of this phi.
200
- bool PhiStorageOptimizer::canCoalesceValue (SILValue incomingVal) {
244
+ SILBasicBlock *
245
+ PhiStorageOptimizer::canCoalesceValue (SILValue incomingVal,
246
+ ProjectedValues *projectedValues) {
201
247
// A Phi must not project from storage that was initialized on a path that
202
248
// reaches the phi because other uses of the storage may interfere with the
203
249
// phi. A phi may, however, be a composing use projection.
@@ -215,31 +261,20 @@ bool PhiStorageOptimizer::canCoalesceValue(SILValue incomingVal) {
215
261
// could be handled, but would require by recursively following uses across
216
262
// projections when computing liveness.
217
263
if (!incomingStorage.isAllocated () || incomingStorage.isProjection ())
218
- return false ;
264
+ return nullptr ;
219
265
220
- auto *defInst = incomingVal->getDefiningInstruction ();
221
- if (!defInst) {
222
- // Indirect function arguments were replaced by loads.
223
- assert (!isa<SILFunctionArgument>(incomingVal));
266
+ if (PhiValue (incomingVal)) {
224
267
// Do not coalesce a phi with other phis. This would require liveness
225
268
// analysis of the whole phi web before coalescing phi operands.
226
- return false ;
269
+ return nullptr ;
227
270
}
228
271
229
272
// Don't coalesce an incoming value unless it's storage is from a stack
230
273
// allocation, which can be replaced with another alloc_stack.
231
274
if (!isa<AllocStackInst>(incomingStorage.storageAddress ))
232
- return false ;
233
-
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 ;
275
+ return nullptr ;
241
276
242
- return true ;
277
+ return getDominatingBlock (incomingVal, projectedValues) ;
243
278
}
244
279
245
280
// Process a single incoming phi operand. Compute the value's liveness while
@@ -248,11 +283,13 @@ void PhiStorageOptimizer::tryCoalesceOperand(SILBasicBlock *incomingPred) {
248
283
Operand *incomingOper = phi.getOperand (incomingPred);
249
284
SILValue incomingVal = incomingOper->get ();
250
285
251
- if (!canCoalesceValue (incomingVal))
286
+ ProjectedValues projectedValues (incomingPred->getFunction ());
287
+ auto *dominatingBlock = canCoalesceValue (incomingVal, &projectedValues);
288
+ if (!dominatingBlock)
252
289
return ;
253
290
254
291
BasicBlockSetVector liveBlocks (getFunction ());
255
- if (!recordUseLiveness (incomingVal , liveBlocks))
292
+ if (!recordUseLiveness (projectedValues, dominatingBlock , liveBlocks))
256
293
return ;
257
294
258
295
for (auto *block : liveBlocks) {
@@ -262,39 +299,100 @@ void PhiStorageOptimizer::tryCoalesceOperand(SILBasicBlock *incomingPred) {
262
299
coalescedPhi.coalescedOperands .push_back (incomingOper);
263
300
}
264
301
265
- // Record liveness generated by uses of \p incomingVal.
302
+ // / The block which dominates all the defs whose storage is projected out of the
303
+ // / storage for \p incomingVal.
304
+ // /
305
+ // / Populates \p projectedValues with the values whose storage is recursively
306
+ // / projected out of the storage for incomingVal.
307
+ SILBasicBlock *
308
+ PhiStorageOptimizer::getDominatingBlock (SILValue incomingVal,
309
+ ProjectedValues *projectedValues) {
310
+ assert (domInfo);
311
+
312
+ // Recursively find the set of value definitions and the dominating LCA.
313
+ ValueWorklist values (incomingVal->getFunction ());
314
+
315
+ values.push (incomingVal);
316
+
317
+ SILBasicBlock *lca = incomingVal->getParentBlock ();
318
+ auto updateLCA = [&](SILBasicBlock *other) {
319
+ lca = domInfo->findNearestCommonDominator (lca, other);
320
+ };
321
+
322
+ while (auto value = values.pop ()) {
323
+ if (projectedValues)
324
+ projectedValues->push_back (value);
325
+ auto *defInst = value->getDefiningInstruction ();
326
+ if (!defInst) {
327
+ assert (!PhiValue (value));
328
+ updateLCA (value->getParentBlock ());
329
+ continue ;
330
+ }
331
+ SmallVector<Operand *, 2 > operands;
332
+ if (findUseProjections (defInst, operands)) {
333
+ // Any operand whose storage is a use-projection out of `value`'s storage
334
+ // dominates `value` (i.e. `defInst`), so skip updating the LCA here.
335
+ for (auto *operand : operands) {
336
+ values.pushIfNotVisited (operand->get ());
337
+ }
338
+ continue ;
339
+ }
340
+ SmallVector<SILValue, 2 > projecteds;
341
+ if (findDefProjections (value, &projecteds)) {
342
+ // Walking up the projection chain from this point, every subsequent
343
+ // projection must be a def projection [projection_chain_structure].
344
+ // Every such projection is dominated by `value` (i.e. defInst).
345
+ //
346
+ // If the walk were only updating the LCA, it could stop here.
347
+ //
348
+ // It is also collecting values whose storage is projected out of the
349
+ // phi, however, so the walk must continue.
350
+ updateLCA (defInst->getParent ());
351
+ for (auto projected : projecteds) {
352
+ values.pushIfNotVisited (projected);
353
+ }
354
+ continue ;
355
+ }
356
+ updateLCA (defInst->getParent ());
357
+ }
358
+ return lca;
359
+ }
360
+
361
+ // Record liveness generated by uses of \p projectedVals.
266
362
//
267
363
// Return true if no interference was detected along the way.
268
- bool PhiStorageOptimizer::recordUseLiveness (SILValue incomingVal,
364
+ bool PhiStorageOptimizer::recordUseLiveness (ProjectedValues &projectedVals,
365
+ SILBasicBlock *dominatingBlock,
269
366
BasicBlockSetVector &liveBlocks) {
270
367
assert (liveBlocks.empty ());
271
368
272
- // Stop liveness traversal at defBB.
273
- SILBasicBlock *defBB = incomingVal->getParentBlock ();
274
- for (auto *use : incomingVal->getUses ()) {
275
- StackList<SILBasicBlock *> liveBBWorklist (getFunction ());
369
+ for (auto projectedVal : projectedVals) {
370
+ for (auto *use : projectedVal->getUses ()) {
371
+ StackList<SILBasicBlock *> liveBBWorklist (getFunction ());
372
+
373
+ // If \p liveBB is already occupied by another value, return
374
+ // false. Otherwise, mark \p liveBB live and push it onto liveBBWorklist.
375
+ auto visitLiveBlock = [&](SILBasicBlock *liveBB) {
376
+ assert (liveBB != phi.phiBlock && " phi operands are consumed" );
276
377
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" );
378
+ if (occupiedBlocks.contains (liveBB))
379
+ return false ;
281
380
282
- if (occupiedBlocks.contains (liveBB))
381
+ // Stop liveness traversal at dominatingBlock.
382
+ if (liveBlocks.insert (liveBB) && liveBB != dominatingBlock) {
383
+ liveBBWorklist.push_back (liveBB);
384
+ }
385
+ return true ;
386
+ };
387
+ if (!visitLiveBlock (use->getUser ()->getParent ()))
283
388
return false ;
284
389
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 ;
390
+ while (!liveBBWorklist.empty ()) {
391
+ auto *succBB = liveBBWorklist.pop_back_val ();
392
+ for (auto *predBB : succBB->getPredecessorBlocks ()) {
393
+ if (!visitLiveBlock (predBB))
394
+ return false ;
395
+ }
298
396
}
299
397
}
300
398
}
0 commit comments