10
10
//
11
11
// ===----------------------------------------------------------------------===//
12
12
// /
13
+ // / This pass contains two optimizations:
14
+ // /
15
+ // / 1) DominatedAccessRemoval:
13
16
// / This function pass removes dynamic access enforcement based on dominance.
14
17
// /
15
18
// / General case:
20
23
// / begin_access A [no_nested_conflict] // dominated by the first access
21
24
// / load/store
22
25
// / end_access A
26
+ // /
23
27
// / The second access scope does not need to be emitted.
24
28
// /
25
- // / Note: This optimization must be aware of all possible access to a Class or
29
+ // / 2) LoopDominatingAccessAdder:
30
+ // / This function pass adds new dominating accesses to loop's dynamic accesses
31
+ // /
32
+ // / General case:
33
+ // / <loop preheader>
34
+ // / A = ref_element_addr
35
+ // / <loop>
36
+ // / begin_access A [dynamic] [no_nested_conflict]
37
+ // /
38
+ // / Adding an empty begin_access A in the preheader would allow us to
39
+ // / turn the loop's access to [static]
40
+ // /
41
+ // / Note 1: This optimization must be aware of all possible access to a Class or
26
42
// / Global address. This includes unpaired access instructions and keypath
27
43
// / entry points. Ignoring any access pattern would weaken enforcement.
44
+ // /
45
+ // / Note 2: We either need to also flag the dominating access specially, or,
46
+ // / we need to do this in the last-chance pipeline, with a guarantee that no,
47
+ // / access marker removal is done after it.
48
+ // /
49
+ // / Note 3: LoopDominatedAccessAdder makes some key assumptions:
50
+ // / 1) Precondition: There are no unpaired accesses (or keypath accesses)
51
+ // / anywhere in the function.
52
+ // / 2) Invariant: An access scope cannot begin outside the loop and end on any
53
+ // / path between the loop header and the original dominated access.
54
+ // / 3) Expectation: Accesses will be hoisted across nested loops during
55
+ // / bottom-up processing.
28
56
// ===----------------------------------------------------------------------===//
29
57
30
58
#define DEBUG_TYPE " access-enforcement-dom"
31
59
32
60
#include " swift/SIL/DebugUtils.h"
33
61
#include " swift/SIL/MemAccessUtils.h"
62
+ #include " swift/SIL/SILBuilder.h"
34
63
#include " swift/SIL/SILFunction.h"
35
64
#include " swift/SILOptimizer/Analysis/DominanceAnalysis.h"
65
+ #include " swift/SILOptimizer/Analysis/LoopAnalysis.h"
36
66
#include " swift/SILOptimizer/PassManager/Transforms.h"
37
67
#include " swift/SILOptimizer/Utils/Local.h"
38
68
@@ -53,7 +83,7 @@ class DominatedAccessRemoval {
53
83
DominatedAccessRemoval (SILFunction &func, DominanceInfo *domInfo)
54
84
: func(func), domInfo(domInfo) {}
55
85
56
- void perform ();
86
+ bool perform ();
57
87
58
88
protected:
59
89
bool visitInstruction (SILInstruction *instr,
@@ -154,6 +184,13 @@ void DominatedAccessRemoval::visitBeginAccess(
154
184
return ;
155
185
}
156
186
187
+ auto *parentBegin = visitedAccessesIt->first ;
188
+ if (beginAccess->getAccessKind () > parentBegin->getAccessKind ()) {
189
+ // we can't change to static: dominating access has a > access kind
190
+ // change parent's access kind so we would be able to do so
191
+ parentBegin->setAccessKind (beginAccess->getAccessKind ());
192
+ }
193
+
157
194
domInstrs.push_back (beginAccess);
158
195
}
159
196
@@ -228,25 +265,198 @@ void DominatedAccessRemoval::optimize() {
228
265
}
229
266
}
230
267
231
- void DominatedAccessRemoval::perform () {
268
+ // Returns a bool: If we should bail on this function
269
+ // we return false - else true
270
+ bool DominatedAccessRemoval::perform () {
232
271
if (func.empty ())
233
- return ;
272
+ return false ;
234
273
235
274
if (!analyze ()) {
236
275
LLVM_DEBUG (llvm::dbgs ()
237
276
<< " Bailed on function: " << func.getName () << " \n " );
238
- return ;
277
+ return false ;
239
278
}
240
279
optimize ();
280
+ return true ;
281
+ }
282
+
283
+ namespace {
284
+ class LoopDominatingAccessAdder {
285
+ SmallVector<SILLoop *, 8 > bottomUpWorkList;
286
+ DominanceInfo *domInfo;
287
+
288
+ public:
289
+ LoopDominatingAccessAdder (SILLoop *topLevelLoop, DominanceInfo *domInfo)
290
+ : domInfo(domInfo) {
291
+ // Collect loops for a recursive bottom-up traversal in the loop tree.
292
+ bottomUpWorkList.push_back (topLevelLoop);
293
+ for (unsigned i = 0 ; i < bottomUpWorkList.size (); ++i) {
294
+ auto *loop = bottomUpWorkList[i];
295
+ for (auto *subLoop : *loop) {
296
+ bottomUpWorkList.push_back (subLoop);
297
+ }
298
+ }
299
+ }
300
+
301
+ void perform ();
302
+
303
+ protected:
304
+ // / \brief Collect a set of instructions that can be dominated
305
+ void
306
+ analyzeCurrentLoop (SILLoop *currentLoop,
307
+ SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec);
308
+
309
+ // / \brief Optimize the current loop nest.
310
+ void optimizeLoop (SILLoop *currnetLoop,
311
+ SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec);
312
+ };
313
+ } // namespace
314
+
315
+ void LoopDominatingAccessAdder::analyzeCurrentLoop (
316
+ SILLoop *currentLoop,
317
+ SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec) {
318
+ auto *preheader = currentLoop->getLoopPreheader ();
319
+ if (!preheader) {
320
+ // Can't dominate this loop - bail
321
+ return ;
322
+ }
323
+ for (auto *block : currentLoop->getBlocks ()) {
324
+ for (auto &instr : *block) {
325
+ auto *beginAccess = dyn_cast<BeginAccessInst>(&instr);
326
+ if (!beginAccess) {
327
+ continue ;
328
+ }
329
+ if (beginAccess->getEnforcement () != SILAccessEnforcement::Dynamic) {
330
+ continue ;
331
+ }
332
+ if (!beginAccess->hasNoNestedConflict ()) {
333
+ continue ;
334
+ }
335
+ auto operand = beginAccess->getOperand ();
336
+ auto *parentBB = operand->getParentBlock ();
337
+ if (currentLoop->contains (parentBB)) {
338
+ // Loop varaint argument
339
+ continue ;
340
+ }
341
+ if (!domInfo->properlyDominates (parentBB, preheader) &&
342
+ parentBB != preheader) {
343
+ // Argument not dominated by the preheader
344
+ // TODO: Should this be a fatal error? Invalid control flow?
345
+ continue ;
346
+ }
347
+ dominatableInstrVec.push_back (beginAccess);
348
+ }
349
+ }
350
+ }
351
+
352
+ void LoopDominatingAccessAdder::optimizeLoop (
353
+ SILLoop *currnetLoop,
354
+ SmallVectorImpl<BeginAccessInst *> &dominatableInstrVec) {
355
+ auto *preheader = currnetLoop->getLoopPreheader ();
356
+ if (!preheader) {
357
+ // Can't dominate this loop - bail
358
+ return ;
359
+ }
360
+ // Get the last instruction in the pre-header, this is
361
+ // We insert new access scopes before it.
362
+ // We also use it for our currently-optimizeable begin_access:
363
+ // We can only create a dominating scope if the
364
+ // dominatableInstr's operand dominates this instruction:
365
+ TermInst *preheaderTerm = preheader->getTerminator ();
366
+
367
+ // Same operand may appear more than once in dominatableInstrVec
368
+ // No reason to create multiple dominating instructions
369
+ // The following set keeps track of operands we can safely skip
370
+ // i.e. we already created a dominating begin_access for
371
+ // We also need to keep track of the new dominating begin_access:
372
+ // First inner loop access might be [read] while a second one
373
+ // Might be [modify], the dominating access should be [modify]
374
+ // in that case
375
+ llvm::SmallDenseMap<SILValue, BeginAccessInst *> domOpToBeginAccess;
376
+
377
+ while (!dominatableInstrVec.empty ()) {
378
+ BeginAccessInst *beginAccess = dominatableInstrVec.pop_back_val ();
379
+ auto operand = beginAccess->getOperand ();
380
+ auto domIt = domOpToBeginAccess.find (operand);
381
+ if (domIt != domOpToBeginAccess.end ()) {
382
+ BeginAccessInst *domBegin = domIt->getSecond ();
383
+ if (domBegin->getAccessKind () < beginAccess->getAccessKind ()) {
384
+ LLVM_DEBUG (llvm::dbgs ()
385
+ << " Changing " << *domBegin << " access kind to "
386
+ << *beginAccess << " access kind\n " );
387
+ domBegin->setAccessKind (beginAccess->getAccessKind ());
388
+ }
389
+ LLVM_DEBUG (llvm::dbgs ()
390
+ << " Already have a dominating access scope for: "
391
+ << *beginAccess
392
+ << " , settting the inner access' enforcement to static\n " );
393
+ beginAccess->setEnforcement (SILAccessEnforcement::Static);
394
+ continue ;
395
+ }
396
+ // We can go ahead with the optimization - do so:
397
+ SILBuilderWithScope scopeBuilder (preheaderTerm);
398
+ BeginAccessInst *newBegin = scopeBuilder.createBeginAccess (
399
+ beginAccess->getLoc (), operand, beginAccess->getAccessKind (),
400
+ SILAccessEnforcement::Dynamic, true /* no nested conflict*/ ,
401
+ beginAccess->isFromBuiltin ());
402
+ scopeBuilder.createEndAccess (beginAccess->getLoc (), newBegin, false );
403
+ domOpToBeginAccess.insert (std::make_pair (operand, newBegin));
404
+ LLVM_DEBUG (llvm::dbgs ()
405
+ << " Created a dominating access scope for: " << *beginAccess
406
+ << " , settting the inner access' enforcement to static\n " );
407
+ beginAccess->setEnforcement (SILAccessEnforcement::Static);
408
+ }
409
+ }
410
+
411
+ void LoopDominatingAccessAdder::perform () {
412
+ // Process loops bottom up in the loop tree.
413
+ while (!bottomUpWorkList.empty ()) {
414
+ SILLoop *currnetLoop = bottomUpWorkList.pop_back_val ();
415
+ LLVM_DEBUG (llvm::dbgs () << " Processing loop " << *currnetLoop);
416
+
417
+ llvm::SmallVector<BeginAccessInst *, 8 > dominatableInstrVec;
418
+ analyzeCurrentLoop (currnetLoop, dominatableInstrVec);
419
+ optimizeLoop (currnetLoop, dominatableInstrVec);
420
+ }
241
421
}
242
422
243
423
namespace {
244
424
struct AccessEnforcementDom : public SILFunctionTransform {
245
425
void run () override {
426
+ SILFunction *func = getFunction ();
427
+
246
428
DominanceAnalysis *domAnalysis = getAnalysis<DominanceAnalysis>();
247
- DominanceInfo *domInfo = domAnalysis->get (getFunction ());
248
- DominatedAccessRemoval eliminationPass (*getFunction (), domInfo);
249
- eliminationPass.perform ();
429
+ DominanceInfo *domInfo = domAnalysis->get (func);
430
+ DominatedAccessRemoval eliminationPass (*func, domInfo);
431
+ if (!eliminationPass.perform ()) {
432
+ // Bailed on the function due to key-path and/or other reasons
433
+ // Too risky to do anything else, don't repeat
434
+ // the analysis in subsequent optimizations
435
+ return ;
436
+ }
437
+
438
+ SILLoopAnalysis *loopAnalysis = PM->getAnalysis <SILLoopAnalysis>();
439
+ SILLoopInfo *loopInfo = loopAnalysis->get (func);
440
+ if (loopInfo->empty ()) {
441
+ LLVM_DEBUG (llvm::dbgs () << " No loops in " << func->getName () << " \n " );
442
+ return ;
443
+ }
444
+
445
+ LLVM_DEBUG (llvm::dbgs () << " LoopDominatingAccessAdder: Processing loops in "
446
+ << func->getName () << " \n " );
447
+
448
+ for (auto *topLevelLoop : *loopInfo) {
449
+ LoopDominatingAccessAdder additionPass (topLevelLoop, domInfo);
450
+ additionPass.perform ();
451
+ }
452
+
453
+ // TODO: Do we need to rerun the normal dominated check removal here?
454
+ // I don’t think we need to do so: there shouldn’t be any redundant
455
+ // accesses in the preheader that we can eliminate:
456
+ // We only create a single access no matter how many accesses to
457
+ // the same storage are inside the loop.
458
+ // If there was an access outside of the loop then no new potential
459
+ // would have been exposed by LoopDominatingAccessAdder?
250
460
}
251
461
};
252
462
} // namespace
0 commit comments