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,45 @@ 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 (SILValue value, SmallVectorImpl<Operand *> &operands);
125
+ bool findDefProjections (SILValue value,
126
+ SmallVectorImpl<SILValue> *projecteds);
127
+ SILBasicBlock *canCoalesceValue (SILValue incomingVal,
128
+ ProjectedValues *projectedValues);
122
129
void tryCoalesceOperand (SILBasicBlock *incomingPred);
123
- bool recordUseLiveness (SILValue incomingVal, BasicBlockSetVector &liveBlocks);
130
+ bool recordUseLiveness (ProjectedValues &incomingVals,
131
+ SILBasicBlock *dominatingBlock,
132
+ BasicBlockSetVector &liveBlocks);
133
+ SILBasicBlock *getDominatingBlock (SILValue incomingVal,
134
+ ProjectedValues *projectedValues);
124
135
};
125
136
126
137
} // namespace swift
127
138
128
139
void CoalescedPhi::coalesce (PhiValue phi,
129
- const ValueStorageMap &valueStorageMap) {
140
+ const ValueStorageMap &valueStorageMap,
141
+ DominanceInfo *domInfo) {
130
142
assert (empty () && " attempt to recoalesce the same phi" );
131
143
132
- PhiStorageOptimizer (phi, valueStorageMap, *this ).optimize ();
144
+ PhiStorageOptimizer (phi, valueStorageMap, *this , domInfo ).optimize ();
133
145
}
134
146
135
147
// / Optimize phi storage by coalescing phi operands.
@@ -168,7 +180,8 @@ void CoalescedPhi::coalesce(PhiValue phi,
168
180
void PhiStorageOptimizer::optimize () {
169
181
// The single incoming value case always projects storage.
170
182
if (auto *predecessor = phi.phiBlock ->getSinglePredecessorBlock ()) {
171
- if (canCoalesceValue (phi.getValue ()->getIncomingPhiValue (predecessor))) {
183
+ if (canCoalesceValue (phi.getValue ()->getIncomingPhiValue (predecessor),
184
+ /* projectedValues=*/ nullptr )) {
172
185
// Storage will always be allocated for the phi. The optimization
173
186
// attempts to let incoming values reuse the phi's storage. This isn't
174
187
// always possible, even in the single incoming value case. For example,
@@ -183,21 +196,52 @@ void PhiStorageOptimizer::optimize() {
183
196
}
184
197
}
185
198
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) {
199
+ // / Return \p value's defining instruction's operand which is a composing use
200
+ // / projection into \p defInst's storage, or nullptr if none exists.
201
+ // /
202
+ // / Precondition: \p value is defined by an instruction.
203
+ bool PhiStorageOptimizer::findUseProjections (
204
+ SILValue value, SmallVectorImpl<Operand *> &operands) {
205
+ auto *defInst = value->getDefiningInstruction ();
206
+ auto ordinal = valueStorageMap.getOrdinal (value);
207
+ bool found = false ;
189
208
for (Operand &oper : defInst->getAllOperands ()) {
190
- if (valueStorageMap.isComposingUseProjection (&oper))
191
- return true ;
209
+ if (valueStorageMap.isComposingUseProjection (&oper) &&
210
+ valueStorageMap.getStorage (oper.get ()).projectedStorageID == ordinal) {
211
+ found = true ;
212
+ operands.push_back (&oper);
213
+ }
192
214
}
193
- return false ;
215
+ return found ;
194
216
}
195
217
196
- // Return true in \p incomingVal can be coalesced with this phi ignoring
197
- // possible interference. Simply determine whether storage reuse is possible.
218
+ bool PhiStorageOptimizer::findDefProjections (
219
+ SILValue value, SmallVectorImpl<SILValue> *projecteds) {
220
+ bool found = false ;
221
+ for (auto user : value->getUsers ()) {
222
+ for (auto result : user->getResults ()) {
223
+ auto *storage = valueStorageMap.getStorageOrNull (result);
224
+ if (!storage)
225
+ continue ;
226
+ if (storage->isDefProjection ) {
227
+ assert (storage->projectedStorageID ==
228
+ valueStorageMap.getOrdinal (value));
229
+ projecteds->push_back (result);
230
+ found = true ;
231
+ }
232
+ }
233
+ }
234
+ return found;
235
+ }
236
+
237
+ // Determine whether storage reuse is possible with \p incomingVal. Ignore
238
+ // possible interference. If reuse is possible, return the block which
239
+ // dominates all defs whose storage is projected out of incomingVal.
198
240
//
199
241
// Precondition: \p incomingVal is an operand of this phi.
200
- bool PhiStorageOptimizer::canCoalesceValue (SILValue incomingVal) {
242
+ SILBasicBlock *
243
+ PhiStorageOptimizer::canCoalesceValue (SILValue incomingVal,
244
+ ProjectedValues *projectedValues) {
201
245
// A Phi must not project from storage that was initialized on a path that
202
246
// reaches the phi because other uses of the storage may interfere with the
203
247
// phi. A phi may, however, be a composing use projection.
@@ -215,31 +259,20 @@ bool PhiStorageOptimizer::canCoalesceValue(SILValue incomingVal) {
215
259
// could be handled, but would require by recursively following uses across
216
260
// projections when computing liveness.
217
261
if (!incomingStorage.isAllocated () || incomingStorage.isProjection ())
218
- return false ;
262
+ return nullptr ;
219
263
220
- auto *defInst = incomingVal->getDefiningInstruction ();
221
- if (!defInst) {
222
- // Indirect function arguments were replaced by loads.
223
- assert (!isa<SILFunctionArgument>(incomingVal));
264
+ if (PhiValue (incomingVal)) {
224
265
// Do not coalesce a phi with other phis. This would require liveness
225
266
// analysis of the whole phi web before coalescing phi operands.
226
- return false ;
267
+ return nullptr ;
227
268
}
228
269
229
270
// Don't coalesce an incoming value unless it's storage is from a stack
230
271
// allocation, which can be replaced with another alloc_stack.
231
272
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 ;
273
+ return nullptr ;
241
274
242
- return true ;
275
+ return getDominatingBlock (incomingVal, projectedValues) ;
243
276
}
244
277
245
278
// Process a single incoming phi operand. Compute the value's liveness while
@@ -248,11 +281,13 @@ void PhiStorageOptimizer::tryCoalesceOperand(SILBasicBlock *incomingPred) {
248
281
Operand *incomingOper = phi.getOperand (incomingPred);
249
282
SILValue incomingVal = incomingOper->get ();
250
283
251
- if (!canCoalesceValue (incomingVal))
284
+ ProjectedValues projectedValues (incomingPred->getFunction ());
285
+ auto *dominatingBlock = canCoalesceValue (incomingVal, &projectedValues);
286
+ if (!dominatingBlock)
252
287
return ;
253
288
254
289
BasicBlockSetVector liveBlocks (getFunction ());
255
- if (!recordUseLiveness (incomingVal , liveBlocks))
290
+ if (!recordUseLiveness (projectedValues, dominatingBlock , liveBlocks))
256
291
return ;
257
292
258
293
for (auto *block : liveBlocks) {
@@ -262,39 +297,102 @@ void PhiStorageOptimizer::tryCoalesceOperand(SILBasicBlock *incomingPred) {
262
297
coalescedPhi.coalescedOperands .push_back (incomingOper);
263
298
}
264
299
265
- // Record liveness generated by uses of \p incomingVal.
300
+ // / The block which dominates all the defs whose storage is projected out of the
301
+ // / storage for \p incomingVal.
302
+ // /
303
+ // / Populates \p projectedValues with the values whose storage is recursively
304
+ // / projected out of the storage for incomingVal.
305
+ SILBasicBlock *
306
+ PhiStorageOptimizer::getDominatingBlock (SILValue incomingVal,
307
+ ProjectedValues *projectedValues) {
308
+ assert (domInfo);
309
+
310
+ // Recursively find the set of value definitions and the dominating LCA.
311
+ ValueWorklist values (incomingVal->getFunction ());
312
+
313
+ values.push (incomingVal);
314
+
315
+ SILBasicBlock *lca = incomingVal->getParentBlock ();
316
+ auto updateLCA = [&](SILBasicBlock *other) {
317
+ lca = domInfo->findNearestCommonDominator (lca, other);
318
+ };
319
+
320
+ while (auto value = values.pop ()) {
321
+ if (projectedValues)
322
+ projectedValues->push_back (value);
323
+ auto *defInst = value->getDefiningInstruction ();
324
+ if (!defInst) {
325
+ assert (!PhiValue (value));
326
+ updateLCA (value->getParentBlock ());
327
+ continue ;
328
+ }
329
+ SmallVector<Operand *, 2 > operands;
330
+ if (findUseProjections (value, operands)) {
331
+ assert (operands.size () > 0 );
332
+ // Any operand whose storage is a use-projection out of `value`'s storage
333
+ // dominates `value` (i.e. `defInst`), so skip updating the LCA here.
334
+ for (auto *operand : operands) {
335
+ values.pushIfNotVisited (operand->get ());
336
+ }
337
+ continue ;
338
+ }
339
+ SmallVector<SILValue, 2 > projecteds;
340
+ if (findDefProjections (value, &projecteds)) {
341
+ assert (projecteds.size () > 0 );
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