|
28 | 28 |
|
29 | 29 | using namespace swift;
|
30 | 30 |
|
| 31 | +// This option allows markers to remain in -Onone as a structural SIL property. |
| 32 | +// Regardless of this option, sufficient markers are always emitted to satisfy |
| 33 | +// the current enforcement level. This options simply allows markers to remain |
| 34 | +// for testing and validation. |
| 35 | +// |
| 36 | +// This option only applies to InactiveAccessMarkerElimination. Any occurrence |
| 37 | +// of FullAccessMarkerElimination in the pass pipeline effectively overrides the |
| 38 | +// options and removes all markers. |
| 39 | +// |
| 40 | +// At -Onone, with EnableMarkers, no static markers are removed. |
| 41 | +// With !EnableMarkers: |
| 42 | +// Enforcement | Static | Dynamic |
| 43 | +// none | Remove after Diag | Remove ASAP |
| 44 | +// unchecked | Remain through IRGen | Remove ASAP |
| 45 | +// checked | Remain through IRGen | Remain through IRGen |
| 46 | +// dynamic-only| Remove after Diag | Remain through IRGen |
| 47 | +llvm::cl::opt<bool> EnableAccessMarkers( |
| 48 | + "sil-access-markers", llvm::cl::init(true), |
| 49 | + llvm::cl::desc("Enable memory access makers that aren't needed for " |
| 50 | + "diagnostics.")); |
| 51 | + |
31 | 52 | namespace {
|
32 | 53 |
|
33 | 54 | struct AccessMarkerElimination : SILModuleTransform {
|
34 | 55 | virtual bool isFullElimination() = 0;
|
35 | 56 |
|
36 |
| - void replaceBeginAccessUsers(BeginAccessInst *beginAccess) { |
| 57 | + bool removedAny = false; |
| 58 | + |
| 59 | + SILBasicBlock::iterator eraseInst(SILInstruction *inst) { |
| 60 | + removedAny = true; |
| 61 | + return inst->getParent()->erase(inst); |
| 62 | + }; |
37 | 63 |
|
38 |
| - // Handle all the uses: |
39 |
| - while (!beginAccess->use_empty()) { |
40 |
| - Operand *op = *beginAccess->use_begin(); |
| 64 | + void replaceBeginAccessUsers(BeginAccessInst *beginAccess); |
41 | 65 |
|
42 |
| - // Delete any associated end_access instructions. |
43 |
| - if (auto endAccess = dyn_cast<EndAccessInst>(op->getUser())) { |
44 |
| - assert(endAccess->use_empty() && "found use of end_access"); |
45 |
| - endAccess->eraseFromParent(); |
| 66 | + // Precondition: !EnableAccessMarkers || isFullElimination() |
| 67 | + bool shouldPreserveAccess(SILAccessEnforcement enforcement); |
46 | 68 |
|
47 |
| - // Forward all other uses to the original address. |
48 |
| - } else { |
49 |
| - op->set(beginAccess->getSource()); |
50 |
| - } |
| 69 | + // Check if the instruction is a marker that should be eliminated. If so, |
| 70 | + // updated the SIL, short of erasing the marker itself, and return true. |
| 71 | + bool checkAndEliminateMarker(SILInstruction *inst); |
| 72 | + |
| 73 | + // Entry point called by the pass manager. |
| 74 | + void run() override; |
| 75 | +}; |
| 76 | + |
| 77 | +void AccessMarkerElimination::replaceBeginAccessUsers( |
| 78 | + BeginAccessInst *beginAccess) { |
| 79 | + // Handle all the uses: |
| 80 | + while (!beginAccess->use_empty()) { |
| 81 | + Operand *op = *beginAccess->use_begin(); |
| 82 | + |
| 83 | + // Delete any associated end_access instructions. |
| 84 | + if (auto endAccess = dyn_cast<EndAccessInst>(op->getUser())) { |
| 85 | + assert(endAccess->use_empty() && "found use of end_access"); |
| 86 | + endAccess->eraseFromParent(); |
| 87 | + |
| 88 | + // Forward all other uses to the original address. |
| 89 | + } else { |
| 90 | + op->set(beginAccess->getSource()); |
51 | 91 | }
|
52 | 92 | }
|
| 93 | +} |
| 94 | + |
| 95 | +// Precondition: !EnableAccessMarkers || isFullElimination() |
| 96 | +bool AccessMarkerElimination::shouldPreserveAccess( |
| 97 | + SILAccessEnforcement enforcement) { |
| 98 | + if (isFullElimination()) |
| 99 | + return false; |
| 100 | + |
| 101 | + auto &M = *getModule(); |
| 102 | + switch (enforcement) { |
| 103 | + case SILAccessEnforcement::Unknown: |
| 104 | + return false; |
| 105 | + case SILAccessEnforcement::Static: |
| 106 | + // Even though static enforcement is already performed, this flag is |
| 107 | + // useful to control marker preservation for now. |
| 108 | + return EnableAccessMarkers || M.getOptions().EnforceExclusivityStatic; |
| 109 | + case SILAccessEnforcement::Dynamic: |
| 110 | + // FIXME: when dynamic markers are fully supported, don't strip: |
| 111 | + // return EnableAccessMarkers || M.getOptions().EnforceExclusivityDynamic; |
| 112 | + return M.getOptions().EnforceExclusivityDynamic; |
| 113 | + case SILAccessEnforcement::Unsafe: |
| 114 | + return false; |
| 115 | + } |
| 116 | +} |
53 | 117 |
|
54 |
| - bool shouldPreserveAccess(SILAccessEnforcement enforcement) { |
55 |
| - auto &M = *getModule(); |
56 |
| - switch (enforcement) { |
57 |
| - case SILAccessEnforcement::Unknown: |
| 118 | +// Check if the instruction is a marker that should be eliminated. If so, |
| 119 | +// updated the SIL, short of erasing the marker itself, and return true. |
| 120 | +bool AccessMarkerElimination::checkAndEliminateMarker(SILInstruction *inst) { |
| 121 | + if (auto beginAccess = dyn_cast<BeginAccessInst>(inst)) { |
| 122 | + // Leave dynamic accesses in place, but delete all others. |
| 123 | + if (shouldPreserveAccess(beginAccess->getEnforcement())) |
58 | 124 | return false;
|
59 |
| - case SILAccessEnforcement::Static: |
60 |
| - // Even though static enforcement is already performed, this flag is |
61 |
| - // useful to control marker preservation for now. |
62 |
| - return M.getOptions().EnforceExclusivityStatic; |
63 |
| - case SILAccessEnforcement::Dynamic: |
64 |
| - return M.getOptions().EnforceExclusivityDynamic; |
65 |
| - case SILAccessEnforcement::Unsafe: |
| 125 | + |
| 126 | + replaceBeginAccessUsers(beginAccess); |
| 127 | + return true; |
| 128 | + } |
| 129 | + |
| 130 | + // end_access instructions will be handled when we process the |
| 131 | + // begin_access. |
| 132 | + |
| 133 | + // begin_unpaired_access instructions will be directly removed and |
| 134 | + // simply replaced with their operand. |
| 135 | + if (auto BUA = dyn_cast<BeginUnpairedAccessInst>(inst)) { |
| 136 | + if (shouldPreserveAccess(BUA->getEnforcement())) |
66 | 137 | return false;
|
67 |
| - } |
68 |
| - }; |
69 | 138 |
|
70 |
| - void run() override { |
71 |
| - auto &M = *getModule(); |
72 |
| - |
73 |
| - bool removedAny = false; |
74 |
| - |
75 |
| - auto eraseInst = [&removedAny](SILInstruction *inst) { |
76 |
| - removedAny = true; |
77 |
| - return inst->getParent()->erase(inst); |
78 |
| - }; |
79 |
| - |
80 |
| - for (auto &F : M) { |
81 |
| - // Iterating in reverse eliminates more begin_access users before they |
82 |
| - // need to be replaced. |
83 |
| - for (auto &BB : reversed(F)) { |
84 |
| - // Don't cache the begin iterator since we're reverse iterating. |
85 |
| - for (auto II = BB.end(); II != BB.begin();) { |
86 |
| - SILInstruction *inst = &*(--II); |
87 |
| - |
88 |
| - if (auto beginAccess = dyn_cast<BeginAccessInst>(inst)) { |
89 |
| - // Leave dynamic accesses in place, but delete all others. |
90 |
| - if (shouldPreserveAccess(beginAccess->getEnforcement())) |
91 |
| - continue; |
92 |
| - |
93 |
| - replaceBeginAccessUsers(beginAccess); |
94 |
| - II = eraseInst(beginAccess); |
95 |
| - continue; |
96 |
| - } |
97 |
| - |
98 |
| - // end_access instructions will be handled when we process the |
99 |
| - // begin_access. |
100 |
| - |
101 |
| - // begin_unpaired_access instructions will be directly removed and |
102 |
| - // simply replaced with their operand. |
103 |
| - if (auto BUA = dyn_cast<BeginUnpairedAccessInst>(inst)) { |
104 |
| - if (shouldPreserveAccess(BUA->getEnforcement())) |
105 |
| - continue; |
106 |
| - |
107 |
| - BUA->replaceAllUsesWith(BUA->getSource()); |
108 |
| - II = eraseInst(BUA); |
109 |
| - continue; |
110 |
| - } |
111 |
| - // end_unpaired_access instructions will be directly removed and |
112 |
| - // simply replaced with their operand. |
113 |
| - if (auto EUA = dyn_cast<EndUnpairedAccessInst>(inst)) { |
114 |
| - if (shouldPreserveAccess(EUA->getEnforcement())) |
115 |
| - continue; |
116 |
| - |
117 |
| - assert(EUA->use_empty() && "use of end_unpaired_access"); |
118 |
| - II = eraseInst(EUA); |
119 |
| - continue; |
120 |
| - } |
121 |
| - } |
122 |
| - } |
| 139 | + BUA->replaceAllUsesWith(BUA->getSource()); |
| 140 | + return true; |
| 141 | + } |
| 142 | + // end_unpaired_access instructions will be directly removed and |
| 143 | + // simply replaced with their operand. |
| 144 | + if (auto EUA = dyn_cast<EndUnpairedAccessInst>(inst)) { |
| 145 | + if (shouldPreserveAccess(EUA->getEnforcement())) |
| 146 | + return false; |
123 | 147 |
|
124 |
| - // Don't invalidate any analyses if we didn't do anything. |
125 |
| - if (!removedAny) |
126 |
| - continue; |
| 148 | + assert(EUA->use_empty() && "use of end_unpaired_access"); |
| 149 | + return true; |
| 150 | + } |
| 151 | + return false; |
| 152 | +} |
127 | 153 |
|
128 |
| - auto InvalidKind = SILAnalysis::InvalidationKind::Instructions; |
129 |
| - invalidateAnalysis(&F, InvalidKind); |
| 154 | +void AccessMarkerElimination::run() { |
| 155 | + // FIXME: When dynamic markers are fully supported, just skip this pass: |
| 156 | + // if (EnableAccessMarkers && !isFullElimination()) |
| 157 | + // return; |
| 158 | + |
| 159 | + auto &M = *getModule(); |
| 160 | + for (auto &F : M) { |
| 161 | + // Iterating in reverse eliminates more begin_access users before they |
| 162 | + // need to be replaced. |
| 163 | + for (auto &BB : reversed(F)) { |
| 164 | + // Don't cache the begin iterator since we're reverse iterating. |
| 165 | + for (auto II = BB.end(); II != BB.begin();) { |
| 166 | + SILInstruction *inst = &*(--II); |
| 167 | + if (checkAndEliminateMarker(inst)) |
| 168 | + II = eraseInst(inst); |
| 169 | + } |
130 | 170 | }
|
| 171 | + |
| 172 | + // Don't invalidate any analyses if we didn't do anything. |
| 173 | + if (!removedAny) |
| 174 | + continue; |
| 175 | + |
| 176 | + auto InvalidKind = SILAnalysis::InvalidationKind::Instructions; |
| 177 | + invalidateAnalysis(&F, InvalidKind); |
131 | 178 | }
|
132 |
| -}; |
| 179 | +} |
133 | 180 |
|
134 | 181 | struct InactiveAccessMarkerElimination : AccessMarkerElimination {
|
135 | 182 | virtual bool isFullElimination() { return false; }
|
|
0 commit comments