10
10
//
11
11
// ===----------------------------------------------------------------------===//
12
12
// /
13
+ // / Pass order dependencies:
14
+ // /
15
+ // / - Will benefit from running after AccessEnforcementSelection.
16
+ // /
17
+ // / - Should run immediately before the AccessEnforcementWMO to share
18
+ // / AccessedStorageAnalysis results.
19
+ // /
13
20
// / This pass optimizes access enforcement as follows:
14
21
// /
15
- // / Access marker folding: Find begin/end access scopes that are uninterrupted
16
- // / by a potential conflicting access. Flag those as [nontracking] access.
22
+ // / **Access marker folding**
23
+ // /
24
+ // / Find begin/end access scopes that are uninterrupted by a potential
25
+ // / conflicting access. Flag those as [nontracking] access.
17
26
// /
18
27
// / Folding must prove that no dynamic conflicts occur inside of an access
19
28
// / scope. That is, a scope has no "nested inner conflicts". The access itself
39
48
// / any path to an access' end of scope has a potentially conflicting access,
40
49
// / then that access is marked as a nested conflict.
41
50
// /
42
- // / Pass order dependencies:
43
- // / - Will benefit from running after AccessEnforcementSelection.
51
+ // / **Local access marker removal**
52
+ // /
53
+ // / When none of the local accesses on local storage (box/stack) have nested
54
+ // / conflicts, then all the local accesses may be disabled by setting their
55
+ // / enforcement to `static`. This is somwhat rare because static diagnostics
56
+ // / already promote the obvious cases to static checks. However, there are two
57
+ // / reasons that dynamic local markers may be disabled: (1) inlining may cause
58
+ // / closure access to become local access (2) local storage may truly escape,
59
+ // / but none of the the local access scopes cross a call site.
44
60
// /
45
61
// / TODO: Perform another run of AccessEnforcementSelection immediately before
46
62
// / this pass. Currently, that pass only works well when run before
@@ -558,14 +574,25 @@ foldNonNestedAccesses(AccessConflictAnalysis::AccessMap &accessMap) {
558
574
return changed;
559
575
}
560
576
561
- // / Eliminate accesses to uniquely identified local storage for which no
577
+ // / Perform local access marker elimination.
578
+ // /
579
+ // / Disable accesses to uniquely identified local storage for which no
562
580
// / accesses can have nested conflicts. This is only valid if the function's
563
- // / local storage cannot be potentially modified by unidentified access.
581
+ // / local storage cannot be potentially modified by unidentified access:
564
582
// /
565
- // / This simply invalidates the AccessMap result rather than erasing individual
566
- // / entries.
583
+ // / - Arguments cannot alias with local storage, so accessing an argument has no
584
+ // / effect on analysis of the current function. When a callee accesses an
585
+ // / argument, AccessedStorageAnalysis will either map the accessed storage to
586
+ // / a value in the caller's function, or mark it as unidentified.
587
+ // /
588
+ // / - Stack or Box local storage could potentially be accessed via Unidentified
589
+ // / access. (Some Unidentified accesses are for initialization or for
590
+ // / temporary storage instead, but those should never have Dynamic
591
+ // / enforcement). These accesses can only be eliminated when there is no
592
+ // / Unidentified access within the function without the [no_nested_conflict]
593
+ // / flag.
567
594
static bool
568
- removeLocalNonNestedAccess (AccessConflictAnalysis::Result & &result,
595
+ removeLocalNonNestedAccess (const AccessConflictAnalysis::Result &result,
569
596
const FunctionAccessedStorage &functionAccess) {
570
597
if (functionAccess.hasUnidentifiedAccess ())
571
598
return false ;
@@ -574,24 +601,18 @@ removeLocalNonNestedAccess(AccessConflictAnalysis::Result &&result,
574
601
SmallVector<BeginAccessInst *, 8 > deadAccesses;
575
602
for (auto &beginAccessAndInfo : result.accessMap ) {
576
603
BeginAccessInst *beginAccess = beginAccessAndInfo.first ;
577
- AccessInfo &info = beginAccessAndInfo.second ;
604
+ const AccessInfo &info = beginAccessAndInfo.second ;
578
605
if (info.seenNestedConflict () || !info.isLocal ())
579
606
continue ;
580
607
581
608
// This particular access to local storage is marked
582
609
// [no_nested_conflict]. Now check FunctionAccessedStorage to determine if
583
610
// that is true for all access to the same storage.
584
- if (functionAccess.hasNoNestedConflict (info))
585
- deadAccesses.push_back (beginAccess);
586
- }
587
- std::sort (deadAccesses.begin (), deadAccesses.end (),
588
- [&result](BeginAccessInst *a, BeginAccessInst *b) {
589
- return result.getAccessIndex (a) < result.getAccessIndex (b);
590
- });
591
- for (BeginAccessInst *beginAccess : deadAccesses) {
592
- DEBUG (llvm::dbgs () << " Removing dead access " << *beginAccess);
593
- changed = true ;
594
- removeBeginAccess (beginAccess);
611
+ if (functionAccess.hasNoNestedConflict (info)) {
612
+ DEBUG (llvm::dbgs () << " Disabling dead access " << *beginAccess);
613
+ beginAccess->setEnforcement (SILAccessEnforcement::Static);
614
+ changed = true ;
615
+ }
595
616
}
596
617
return changed;
597
618
}
@@ -618,15 +639,14 @@ struct AccessEnforcementOpts : public SILFunctionTransform {
618
639
619
640
// Use the updated AccessedStorageAnalysis to find any uniquely identified
620
641
// local storage that has no nested conflict on any of its accesses within
621
- // this function. These can be removed entirely .
642
+ // this function. All the accesses can be marked as statically enforced .
622
643
//
623
644
// Note that the storage address may be passed as an argument and there may
624
645
// be nested conflicts within that call, but none of the accesses within
625
646
// this function will overlap.
626
647
const FunctionAccessedStorage &functionAccess = ASA->getEffects (F);
627
- if (removeLocalNonNestedAccess (std::move ( result) , functionAccess))
648
+ if (removeLocalNonNestedAccess (result, functionAccess))
628
649
invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
629
- // `result` is now invalid.
630
650
}
631
651
};
632
652
} // namespace
0 commit comments