1 //===- LocalAliasAnalysis.cpp - Local stateless alias Analysis for MLIR ---===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "mlir/Analysis/AliasAnalysis/LocalAliasAnalysis.h" 10 11 #include "mlir/IR/FunctionSupport.h" 12 #include "mlir/IR/Matchers.h" 13 #include "mlir/Interfaces/ControlFlowInterfaces.h" 14 #include "mlir/Interfaces/SideEffectInterfaces.h" 15 #include "mlir/Interfaces/ViewLikeInterface.h" 16 17 using namespace mlir; 18 19 //===----------------------------------------------------------------------===// 20 // Underlying Address Computation 21 //===----------------------------------------------------------------------===// 22 23 /// The maximum depth that will be searched when trying to find an underlying 24 /// value. 25 static constexpr unsigned maxUnderlyingValueSearchDepth = 10; 26 27 /// Given a value, collect all of the underlying values being addressed. 28 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth, 29 DenseSet<Value> &visited, 30 SmallVectorImpl<Value> &output); 31 32 /// Given a successor (`region`) of a RegionBranchOpInterface, collect all of 33 /// the underlying values being addressed by one of the successor inputs. If the 34 /// provided `region` is null, as per `RegionBranchOpInterface` this represents 35 /// the parent operation. 36 static void collectUnderlyingAddressValues(RegionBranchOpInterface branch, 37 Region *region, Value inputValue, 38 unsigned inputIndex, 39 unsigned maxDepth, 40 DenseSet<Value> &visited, 41 SmallVectorImpl<Value> &output) { 42 // Given the index of a region of the branch (`predIndex`), or None to 43 // represent the parent operation, try to return the index into the outputs of 44 // this region predecessor that correspond to the input values of `region`. If 45 // an index could not be found, None is returned instead. 46 auto getOperandIndexIfPred = 47 [&](Optional<unsigned> predIndex) -> Optional<unsigned> { 48 SmallVector<RegionSuccessor, 2> successors; 49 branch.getSuccessorRegions(predIndex, successors); 50 for (RegionSuccessor &successor : successors) { 51 if (successor.getSuccessor() != region) 52 continue; 53 // Check that the successor inputs map to the given input value. 54 ValueRange inputs = successor.getSuccessorInputs(); 55 if (inputs.empty()) { 56 output.push_back(inputValue); 57 break; 58 } 59 unsigned firstInputIndex, lastInputIndex; 60 if (region) { 61 firstInputIndex = inputs[0].cast<BlockArgument>().getArgNumber(); 62 lastInputIndex = inputs.back().cast<BlockArgument>().getArgNumber(); 63 } else { 64 firstInputIndex = inputs[0].cast<OpResult>().getResultNumber(); 65 lastInputIndex = inputs.back().cast<OpResult>().getResultNumber(); 66 } 67 if (firstInputIndex > inputIndex || lastInputIndex < inputIndex) { 68 output.push_back(inputValue); 69 break; 70 } 71 return inputIndex - firstInputIndex; 72 } 73 return llvm::None; 74 }; 75 76 // Check branches from the parent operation. 77 Optional<unsigned> regionIndex; 78 if (region) { 79 // Determine the actual region number from the passed region. 80 regionIndex = region->getRegionNumber(); 81 if (Optional<unsigned> operandIndex = 82 getOperandIndexIfPred(/*predIndex=*/llvm::None)) { 83 collectUnderlyingAddressValues( 84 branch.getSuccessorEntryOperands(*regionIndex)[*operandIndex], 85 maxDepth, visited, output); 86 } 87 } 88 // Check branches from each child region. 89 Operation *op = branch.getOperation(); 90 for (int i = 0, e = op->getNumRegions(); i != e; ++i) { 91 if (Optional<unsigned> operandIndex = getOperandIndexIfPred(i)) { 92 for (Block &block : op->getRegion(i)) { 93 Operation *term = block.getTerminator(); 94 // Try to determine possible region-branch successor operands for the 95 // current region. 96 auto successorOperands = 97 getRegionBranchSuccessorOperands(term, regionIndex); 98 if (successorOperands) { 99 collectUnderlyingAddressValues((*successorOperands)[*operandIndex], 100 maxDepth, visited, output); 101 } else if (term->getNumSuccessors()) { 102 // Otherwise, if this terminator may exit the region we can't make 103 // any assumptions about which values get passed. 104 output.push_back(inputValue); 105 return; 106 } 107 } 108 } 109 } 110 } 111 112 /// Given a result, collect all of the underlying values being addressed. 113 static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth, 114 DenseSet<Value> &visited, 115 SmallVectorImpl<Value> &output) { 116 Operation *op = result.getOwner(); 117 118 // If this is a view, unwrap to the source. 119 if (ViewLikeOpInterface view = dyn_cast<ViewLikeOpInterface>(op)) 120 return collectUnderlyingAddressValues(view.getViewSource(), maxDepth, 121 visited, output); 122 // Check to see if we can reason about the control flow of this op. 123 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) { 124 return collectUnderlyingAddressValues(branch, /*region=*/nullptr, result, 125 result.getResultNumber(), maxDepth, 126 visited, output); 127 } 128 129 output.push_back(result); 130 } 131 132 /// Given a block argument, collect all of the underlying values being 133 /// addressed. 134 static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth, 135 DenseSet<Value> &visited, 136 SmallVectorImpl<Value> &output) { 137 Block *block = arg.getOwner(); 138 unsigned argNumber = arg.getArgNumber(); 139 140 // Handle the case of a non-entry block. 141 if (!block->isEntryBlock()) { 142 for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) { 143 auto branch = dyn_cast<BranchOpInterface>((*it)->getTerminator()); 144 if (!branch) { 145 // We can't analyze the control flow, so bail out early. 146 output.push_back(arg); 147 return; 148 } 149 150 // Try to get the operand passed for this argument. 151 unsigned index = it.getSuccessorIndex(); 152 Optional<OperandRange> operands = branch.getSuccessorOperands(index); 153 if (!operands) { 154 // We can't analyze the control flow, so bail out early. 155 output.push_back(arg); 156 return; 157 } 158 collectUnderlyingAddressValues((*operands)[argNumber], maxDepth, visited, 159 output); 160 } 161 return; 162 } 163 164 // Otherwise, check to see if we can reason about the control flow of this op. 165 Region *region = block->getParent(); 166 Operation *op = region->getParentOp(); 167 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) { 168 return collectUnderlyingAddressValues(branch, region, arg, argNumber, 169 maxDepth, visited, output); 170 } 171 172 // We can't reason about the underlying address of this argument. 173 output.push_back(arg); 174 } 175 176 /// Given a value, collect all of the underlying values being addressed. 177 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth, 178 DenseSet<Value> &visited, 179 SmallVectorImpl<Value> &output) { 180 // Check that we don't infinitely recurse. 181 if (!visited.insert(value).second) 182 return; 183 if (maxDepth == 0) { 184 output.push_back(value); 185 return; 186 } 187 --maxDepth; 188 189 if (BlockArgument arg = value.dyn_cast<BlockArgument>()) 190 return collectUnderlyingAddressValues(arg, maxDepth, visited, output); 191 collectUnderlyingAddressValues(value.cast<OpResult>(), maxDepth, visited, 192 output); 193 } 194 195 /// Given a value, collect all of the underlying values being addressed. 196 static void collectUnderlyingAddressValues(Value value, 197 SmallVectorImpl<Value> &output) { 198 DenseSet<Value> visited; 199 collectUnderlyingAddressValues(value, maxUnderlyingValueSearchDepth, visited, 200 output); 201 } 202 203 //===----------------------------------------------------------------------===// 204 // LocalAliasAnalysis: alias 205 //===----------------------------------------------------------------------===// 206 207 /// Given a value, try to get an allocation effect attached to it. If 208 /// successful, `allocEffect` is populated with the effect. If an effect was 209 /// found, `allocScopeOp` is also specified if a parent operation of `value` 210 /// could be identified that bounds the scope of the allocated value; i.e. if 211 /// non-null it specifies the parent operation that the allocation does not 212 /// escape. If no scope is found, `allocScopeOp` is set to nullptr. 213 static LogicalResult 214 getAllocEffectFor(Value value, Optional<MemoryEffects::EffectInstance> &effect, 215 Operation *&allocScopeOp) { 216 // Try to get a memory effect interface for the parent operation. 217 Operation *op; 218 if (BlockArgument arg = value.dyn_cast<BlockArgument>()) 219 op = arg.getOwner()->getParentOp(); 220 else 221 op = value.cast<OpResult>().getOwner(); 222 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op); 223 if (!interface) 224 return failure(); 225 226 // Try to find an allocation effect on the resource. 227 if (!(effect = interface.getEffectOnValue<MemoryEffects::Allocate>(value))) 228 return failure(); 229 230 // If we found an allocation effect, try to find a scope for the allocation. 231 // If the resource of this allocation is automatically scoped, find the parent 232 // operation that bounds the allocation scope. 233 if (llvm::isa<SideEffects::AutomaticAllocationScopeResource>( 234 effect->getResource())) { 235 allocScopeOp = op->getParentWithTrait<OpTrait::AutomaticAllocationScope>(); 236 return success(); 237 } 238 239 // TODO: Here we could look at the users to see if the resource is either 240 // freed on all paths within the region, or is just not captured by anything. 241 // For now assume allocation scope to the function scope (we don't care if 242 // pointer escape outside function). 243 allocScopeOp = op->getParentWithTrait<OpTrait::FunctionLike>(); 244 return success(); 245 } 246 247 /// Given the two values, return their aliasing behavior. 248 static AliasResult aliasImpl(Value lhs, Value rhs) { 249 if (lhs == rhs) 250 return AliasResult::MustAlias; 251 Operation *lhsAllocScope = nullptr, *rhsAllocScope = nullptr; 252 Optional<MemoryEffects::EffectInstance> lhsAlloc, rhsAlloc; 253 254 // Handle the case where lhs is a constant. 255 Attribute lhsAttr, rhsAttr; 256 if (matchPattern(lhs, m_Constant(&lhsAttr))) { 257 // TODO: This is overly conservative. Two matching constants don't 258 // necessarily map to the same address. For example, if the two values 259 // correspond to different symbols that both represent a definition. 260 if (matchPattern(rhs, m_Constant(&rhsAttr))) 261 return AliasResult::MayAlias; 262 263 // Try to find an alloc effect on rhs. If an effect was found we can't 264 // alias, otherwise we might. 265 return succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope)) 266 ? AliasResult::NoAlias 267 : AliasResult::MayAlias; 268 } 269 // Handle the case where rhs is a constant. 270 if (matchPattern(rhs, m_Constant(&rhsAttr))) { 271 // Try to find an alloc effect on lhs. If an effect was found we can't 272 // alias, otherwise we might. 273 return succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope)) 274 ? AliasResult::NoAlias 275 : AliasResult::MayAlias; 276 } 277 278 // Otherwise, neither of the values are constant so check to see if either has 279 // an allocation effect. 280 bool lhsHasAlloc = succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope)); 281 bool rhsHasAlloc = succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope)); 282 if (lhsHasAlloc == rhsHasAlloc) { 283 // If both values have an allocation effect we know they don't alias, and if 284 // neither have an effect we can't make an assumptions. 285 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias; 286 } 287 288 // When we reach this point we have one value with a known allocation effect, 289 // and one without. Move the one with the effect to the lhs to make the next 290 // checks simpler. 291 if (rhsHasAlloc) { 292 std::swap(lhs, rhs); 293 lhsAlloc = rhsAlloc; 294 lhsAllocScope = rhsAllocScope; 295 } 296 297 // If the effect has a scoped allocation region, check to see if the 298 // non-effect value is defined above that scope. 299 if (lhsAllocScope) { 300 // If the parent operation of rhs is an ancestor of the allocation scope, or 301 // if rhs is an entry block argument of the allocation scope we know the two 302 // values can't alias. 303 Operation *rhsParentOp = rhs.getParentRegion()->getParentOp(); 304 if (rhsParentOp->isProperAncestor(lhsAllocScope)) 305 return AliasResult::NoAlias; 306 if (rhsParentOp == lhsAllocScope) { 307 BlockArgument rhsArg = rhs.dyn_cast<BlockArgument>(); 308 if (rhsArg && rhs.getParentBlock()->isEntryBlock()) 309 return AliasResult::NoAlias; 310 } 311 } 312 313 // If we couldn't reason about the relationship between the two values, 314 // conservatively assume they might alias. 315 return AliasResult::MayAlias; 316 } 317 318 /// Given the two values, return their aliasing behavior. 319 AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) { 320 if (lhs == rhs) 321 return AliasResult::MustAlias; 322 323 // Get the underlying values being addressed. 324 SmallVector<Value, 8> lhsValues, rhsValues; 325 collectUnderlyingAddressValues(lhs, lhsValues); 326 collectUnderlyingAddressValues(rhs, rhsValues); 327 328 // If we failed to collect for either of the values somehow, conservatively 329 // assume they may alias. 330 if (lhsValues.empty() || rhsValues.empty()) 331 return AliasResult::MayAlias; 332 333 // Check the alias results against each of the underlying values. 334 Optional<AliasResult> result; 335 for (Value lhsVal : lhsValues) { 336 for (Value rhsVal : rhsValues) { 337 AliasResult nextResult = aliasImpl(lhsVal, rhsVal); 338 result = result ? result->merge(nextResult) : nextResult; 339 } 340 } 341 342 // We should always have a valid result here. 343 return *result; 344 } 345 346 //===----------------------------------------------------------------------===// 347 // LocalAliasAnalysis: getModRef 348 //===----------------------------------------------------------------------===// 349 350 ModRefResult LocalAliasAnalysis::getModRef(Operation *op, Value location) { 351 // Check to see if this operation relies on nested side effects. 352 if (op->hasTrait<OpTrait::HasRecursiveSideEffects>()) { 353 // TODO: To check recursive operations we need to check all of the nested 354 // operations, which can result in a quadratic number of queries. We should 355 // introduce some caching of some kind to help alleviate this, especially as 356 // this caching could be used in other areas of the codebase (e.g. when 357 // checking `wouldOpBeTriviallyDead`). 358 return ModRefResult::getModAndRef(); 359 } 360 361 // Otherwise, check to see if this operation has a memory effect interface. 362 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op); 363 if (!interface) 364 return ModRefResult::getModAndRef(); 365 366 // Build a ModRefResult by merging the behavior of the effects of this 367 // operation. 368 SmallVector<MemoryEffects::EffectInstance> effects; 369 interface.getEffects(effects); 370 371 ModRefResult result = ModRefResult::getNoModRef(); 372 for (const MemoryEffects::EffectInstance &effect : effects) { 373 if (isa<MemoryEffects::Allocate, MemoryEffects::Free>(effect.getEffect())) 374 continue; 375 376 // Check for an alias between the effect and our memory location. 377 // TODO: Add support for checking an alias with a symbol reference. 378 AliasResult aliasResult = AliasResult::MayAlias; 379 if (Value effectValue = effect.getValue()) 380 aliasResult = alias(effectValue, location); 381 382 // If we don't alias, ignore this effect. 383 if (aliasResult.isNo()) 384 continue; 385 386 // Merge in the corresponding mod or ref for this effect. 387 if (isa<MemoryEffects::Read>(effect.getEffect())) { 388 result = result.merge(ModRefResult::getRef()); 389 } else { 390 assert(isa<MemoryEffects::Write>(effect.getEffect())); 391 result = result.merge(ModRefResult::getMod()); 392 } 393 if (result.isModAndRef()) 394 break; 395 } 396 return result; 397 } 398