|
| 1 | +//===--- AccessEnforcementSelection.cpp - Select access enforcement -------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | +/// |
| 13 | +/// This pass eliminates 'unknown' access enforcement by selecting either |
| 14 | +/// static or dynamic enforcement. |
| 15 | +/// |
| 16 | +//===----------------------------------------------------------------------===// |
| 17 | + |
| 18 | +#define DEBUG_TYPE "access-enforcement-selection" |
| 19 | +#include "swift/SIL/SILFunction.h" |
| 20 | +#include "swift/SILOptimizer/PassManager/Transforms.h" |
| 21 | + |
| 22 | +using namespace swift; |
| 23 | + |
| 24 | +static void setStaticEnforcement(BeginAccessInst *access) { |
| 25 | + // TODO: delete if we're not using static enforcement? |
| 26 | + access->setEnforcement(SILAccessEnforcement::Static); |
| 27 | +} |
| 28 | + |
| 29 | +static void setDynamicEnforcement(BeginAccessInst *access) { |
| 30 | + // TODO: delete if we're not using dynamic enforcement? |
| 31 | + access->setEnforcement(SILAccessEnforcement::Dynamic); |
| 32 | +} |
| 33 | + |
| 34 | +namespace { |
| 35 | +class SelectEnforcement { |
| 36 | + AllocBoxInst *Box; |
| 37 | + |
| 38 | + /// A state for tracking escape information about a variable. |
| 39 | + /// StateMap only has entries for blocks for which the variable |
| 40 | + /// has potentially escaped at exit. |
| 41 | + struct State { |
| 42 | + bool IsInWorklist = false; |
| 43 | + |
| 44 | + // At least one of the following must be true. |
| 45 | + bool HasEscape = false; |
| 46 | + bool HasPotentiallyEscapedAtEntry = false; |
| 47 | + |
| 48 | + // In a more advanced problem, this could easily be passed a State. |
| 49 | + bool adjustForEscapeInPredecessor() { |
| 50 | + bool updateSuccessors = false; |
| 51 | + |
| 52 | + if (!HasPotentiallyEscapedAtEntry) { |
| 53 | + HasPotentiallyEscapedAtEntry = true; |
| 54 | + updateSuccessors = !HasEscape; |
| 55 | + } |
| 56 | + |
| 57 | + return updateSuccessors; |
| 58 | + } |
| 59 | + }; |
| 60 | + llvm::DenseMap<SILBasicBlock*, State> StateMap; |
| 61 | + |
| 62 | + /// All the accesses in the function. |
| 63 | + SmallVector<BeginAccessInst*, 8> Accesses; |
| 64 | + |
| 65 | + /// All the escapes in the function. |
| 66 | + SmallPtrSet<SILInstruction*, 8> Escapes; |
| 67 | + |
| 68 | + /// A worklist we use for various purposes. |
| 69 | + SmallVector<SILBasicBlock*, 8> Worklist; |
| 70 | + |
| 71 | +public: |
| 72 | + SelectEnforcement(AllocBoxInst *box) : Box(box) {} |
| 73 | + |
| 74 | + void run(); |
| 75 | + |
| 76 | +private: |
| 77 | + void analyzeUsesOfBox(); |
| 78 | + void analyzeProjection(ProjectBoxInst *projection); |
| 79 | + |
| 80 | + void propagateEscapes(); |
| 81 | + void propagateEscapesFrom(SILBasicBlock *bb); |
| 82 | + |
| 83 | + bool hasPotentiallyEscapedAt(SILInstruction *inst); |
| 84 | + bool hasPotentiallyEscapedAtAnyReachableBlock(BeginAccessInst *access); |
| 85 | + |
| 86 | + void updateAccesses(); |
| 87 | + void updateAccess(BeginAccessInst *access); |
| 88 | +}; |
| 89 | +} // end anonymous namespace |
| 90 | + |
| 91 | +void SelectEnforcement::run() { |
| 92 | + // Set up the data-flow problem. |
| 93 | + analyzeUsesOfBox(); |
| 94 | + |
| 95 | + // Run the data-flow problem. |
| 96 | + propagateEscapes(); |
| 97 | + |
| 98 | + // Update all the accesses. |
| 99 | + updateAccesses(); |
| 100 | +} |
| 101 | + |
| 102 | +void SelectEnforcement::analyzeUsesOfBox() { |
| 103 | + // Collect accesses rooted off of projections. |
| 104 | + for (auto use : Box->getUses()) { |
| 105 | + auto user = use->getUser(); |
| 106 | + |
| 107 | + if (auto projection = dyn_cast<ProjectBoxInst>(user)) { |
| 108 | + analyzeProjection(projection); |
| 109 | + continue; |
| 110 | + } |
| 111 | + |
| 112 | + // Ignore certain other uses that do not capture the value. |
| 113 | + if (isa<StrongRetainInst>(user) || |
| 114 | + isa<StrongReleaseInst>(user) || |
| 115 | + isa<DestroyValueInst>(user) || |
| 116 | + isa<DeallocBoxInst>(user)) |
| 117 | + continue; |
| 118 | + |
| 119 | + // Treat everything else as an escape: |
| 120 | + |
| 121 | + // Add it to the escapes set. |
| 122 | + Escapes.insert(user); |
| 123 | + |
| 124 | + // |
| 125 | + auto userBB = user->getParent(); |
| 126 | + auto &state = StateMap[userBB]; |
| 127 | + if (!state.IsInWorklist) { |
| 128 | + state.HasEscape = true; |
| 129 | + state.IsInWorklist = true; |
| 130 | + Worklist.push_back(userBB); |
| 131 | + } |
| 132 | + assert(state.HasEscape); |
| 133 | + assert(state.IsInWorklist); |
| 134 | + } |
| 135 | + |
| 136 | + assert(!Accesses.empty() && "didn't find original access!"); |
| 137 | +} |
| 138 | + |
| 139 | +void SelectEnforcement::analyzeProjection(ProjectBoxInst *projection) { |
| 140 | + for (auto use : projection->getUses()) { |
| 141 | + auto user = use->getUser(); |
| 142 | + |
| 143 | + // Collect accesses. |
| 144 | + if (auto access = dyn_cast<BeginAccessInst>(user)) { |
| 145 | + if (access->getEnforcement() == SILAccessEnforcement::Unknown) |
| 146 | + Accesses.push_back(access); |
| 147 | + } |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +void SelectEnforcement::propagateEscapes() { |
| 152 | + while (!Worklist.empty()) { |
| 153 | + auto bb = Worklist.pop_back_val(); |
| 154 | + auto it = StateMap.find(bb); |
| 155 | + assert(it != StateMap.end() && |
| 156 | + "block was in worklist but doesn't have a tracking state"); |
| 157 | + auto &state = it->second; |
| 158 | + assert(state.HasEscape || state.HasPotentiallyEscapedAtEntry); |
| 159 | + state.IsInWorklist = false; |
| 160 | + propagateEscapesFrom(bb); |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +/// Given that the box potentially escaped before we exited the |
| 165 | +/// given block, propagate that information to all of its successors. |
| 166 | +void SelectEnforcement::propagateEscapesFrom(SILBasicBlock *bb) { |
| 167 | + assert(StateMap.count(bb)); |
| 168 | + |
| 169 | + // Iterate over the successors of the block. |
| 170 | + for (SILBasicBlock *succ : bb->getSuccessors()) { |
| 171 | + auto &succState = StateMap[succ]; |
| 172 | + |
| 173 | + // If updating the successor changes it in a way that will |
| 174 | + // require us to update its successors, add it to the worklist. |
| 175 | + if (succState.adjustForEscapeInPredecessor()) { |
| 176 | + if (!succState.IsInWorklist) { |
| 177 | + succState.IsInWorklist = true; |
| 178 | + Worklist.push_back(succ); |
| 179 | + } |
| 180 | + } |
| 181 | + } |
| 182 | +} |
| 183 | + |
| 184 | +bool SelectEnforcement::hasPotentiallyEscapedAt(SILInstruction *point) { |
| 185 | + auto bb = point->getParent(); |
| 186 | + |
| 187 | + // If we're not tracking anything for the whole block containing |
| 188 | + // the instruction, we're done; it hasn't escaped here. |
| 189 | + auto it = StateMap.find(bb); |
| 190 | + if (it == StateMap.end()) |
| 191 | + return false; |
| 192 | + |
| 193 | + // If the tracking information says there are escapes before entry, |
| 194 | + // we're done; it has potentially escaped. |
| 195 | + const auto &state = it->second; |
| 196 | + if (state.HasPotentiallyEscapedAtEntry) |
| 197 | + return true; |
| 198 | + |
| 199 | + // Okay, there must be an escape within this block. |
| 200 | + assert(state.HasEscape); |
| 201 | + for (auto ii = point->getIterator(), ie = bb->begin(); ii != ie; ) { |
| 202 | + auto inst = &*--ii; |
| 203 | + |
| 204 | + // Maybe just record the first escape in the block and see if we |
| 205 | + // come after it? |
| 206 | + if (Escapes.count(inst)) |
| 207 | + return true; |
| 208 | + } |
| 209 | + |
| 210 | + return false; |
| 211 | +} |
| 212 | + |
| 213 | +bool SelectEnforcement::hasPotentiallyEscapedAtAnyReachableBlock( |
| 214 | + BeginAccessInst *access) { |
| 215 | + // Fast path: we're not tracking any escapes. (But the box should |
| 216 | + // probably have been promoted to the stack in this case.) |
| 217 | + if (StateMap.empty()) |
| 218 | + return false; |
| 219 | + |
| 220 | + auto bb = access->getParent(); |
| 221 | + |
| 222 | + assert(Worklist.empty()); |
| 223 | + SmallPtrSet<SILBasicBlock*, 8> visited; |
| 224 | + visited.insert(bb); |
| 225 | + Worklist.push_back(bb); |
| 226 | + |
| 227 | + while (!Worklist.empty()) { |
| 228 | + bb = Worklist.pop_back_val(); |
| 229 | + assert(visited.count(bb)); |
| 230 | + |
| 231 | + // If we're tracking information for this block, there's an escape. |
| 232 | + if (StateMap.count(bb)) |
| 233 | + return true; |
| 234 | + |
| 235 | + // Add all reachable successors. |
| 236 | + for (SILBasicBlock *succ : bb->getSuccessors()) { |
| 237 | + if (visited.insert(succ).second) |
| 238 | + Worklist.push_back(succ); |
| 239 | + } |
| 240 | + } |
| 241 | + |
| 242 | + // No reachable block has an escape. |
| 243 | + return false; |
| 244 | +} |
| 245 | + |
| 246 | +void SelectEnforcement::updateAccesses() { |
| 247 | + for (auto access : Accesses) { |
| 248 | + updateAccess(access); |
| 249 | + } |
| 250 | +} |
| 251 | + |
| 252 | +void SelectEnforcement::updateAccess(BeginAccessInst *access) { |
| 253 | + assert(access->getEnforcement() == SILAccessEnforcement::Unknown); |
| 254 | + |
| 255 | + // Check whether the variable escaped before any of the end_accesses. |
| 256 | + bool anyDynamic = false; |
| 257 | + bool hasEndAccess = false; |
| 258 | + for (auto endAccess : access->getEndAccesses()) { |
| 259 | + hasEndAccess = true; |
| 260 | + if (hasPotentiallyEscapedAt(endAccess)) { |
| 261 | + anyDynamic = true; |
| 262 | + break; |
| 263 | + } |
| 264 | + } |
| 265 | + |
| 266 | + // If so, make the access dynamic. |
| 267 | + if (anyDynamic) |
| 268 | + return setDynamicEnforcement(access); |
| 269 | + |
| 270 | + // Otherwise, if there are no end_access instructions, |
| 271 | + // check the terminators of every reachable block. |
| 272 | + if (!hasEndAccess) { |
| 273 | + if (hasPotentiallyEscapedAtAnyReachableBlock(access)) |
| 274 | + return setDynamicEnforcement(access); |
| 275 | + } |
| 276 | + |
| 277 | + // Otherwise, use static enforcement. |
| 278 | + setStaticEnforcement(access); |
| 279 | +} |
| 280 | + |
| 281 | +namespace { |
| 282 | + |
| 283 | +/// The pass. |
| 284 | +struct AccessEnforcementSelection : SILFunctionTransform { |
| 285 | + void run() override { |
| 286 | + for (auto &bb : *getFunction()) { |
| 287 | + for (auto ii = bb.begin(), ie = bb.end(); ii != ie; ) { |
| 288 | + SILInstruction *inst = &*ii; |
| 289 | + ++ii; |
| 290 | + |
| 291 | + if (auto access = dyn_cast<BeginAccessInst>(inst)) { |
| 292 | + handleAccess(access); |
| 293 | + } |
| 294 | + } |
| 295 | + } |
| 296 | + } |
| 297 | + |
| 298 | + void handleAccess(BeginAccessInst *access) { |
| 299 | + if (access->getEnforcement() != SILAccessEnforcement::Unknown) |
| 300 | + return; |
| 301 | + |
| 302 | + auto address = access->getOperand(); |
| 303 | + assert(!isa<MarkUninitializedInst>(address) && |
| 304 | + "pass should be run after definite initialization"); |
| 305 | + |
| 306 | + if (auto box = dyn_cast<ProjectBoxInst>(address)) { |
| 307 | + return handleAccessToBox(access, box); |
| 308 | + } |
| 309 | + |
| 310 | + // If we're not accessing a box, we must've lowered to |
| 311 | + // a stack element. |
| 312 | + assert(isa<AllocStackInst>(address)); |
| 313 | + setStaticEnforcement(access); |
| 314 | + } |
| 315 | + |
| 316 | + void handleAccessToBox(BeginAccessInst *access, ProjectBoxInst *projection) { |
| 317 | + // If we didn't allocate the box, assume that we need to use |
| 318 | + // dynamic enforcement. |
| 319 | + // TODO: use static enforcement in certain provable cases. |
| 320 | + auto box = dyn_cast<AllocBoxInst>(projection->getOperand()); |
| 321 | + if (!box) { |
| 322 | + setDynamicEnforcement(access); |
| 323 | + return; |
| 324 | + } |
| 325 | + |
| 326 | + SelectEnforcement(box).run(); |
| 327 | + } |
| 328 | +}; |
| 329 | + |
| 330 | +} // end anonymous namespace |
| 331 | + |
| 332 | +SILTransform *swift::createAccessEnforcementSelection() { |
| 333 | + return new AccessEnforcementSelection(); |
| 334 | +} |
0 commit comments