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/Matchers.h" 12 #include "mlir/Interfaces/ControlFlowInterfaces.h" 13 #include "mlir/Interfaces/SideEffectInterfaces.h" 14 #include "mlir/Interfaces/ViewLikeInterface.h" 15 16 using namespace mlir; 17 18 //===----------------------------------------------------------------------===// 19 // Underlying Address Computation 20 //===----------------------------------------------------------------------===// 21 22 /// The maximum depth that will be searched when trying to find an underlying 23 /// value. 24 static constexpr unsigned maxUnderlyingValueSearchDepth = 10; 25 26 /// Given a value, collect all of the underlying values being addressed. 27 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth, 28 DenseSet<Value> &visited, 29 SmallVectorImpl<Value> &output); 30 31 /// Given a successor (`region`) of a RegionBranchOpInterface, collect all of 32 /// the underlying values being addressed by one of the successor inputs. If the 33 /// provided `region` is null, as per `RegionBranchOpInterface` this represents 34 /// the parent operation. 35 static void collectUnderlyingAddressValues(RegionBranchOpInterface branch, 36 Region *region, Value inputValue, 37 unsigned inputIndex, 38 unsigned maxDepth, 39 DenseSet<Value> &visited, 40 SmallVectorImpl<Value> &output) { 41 // Given the index of a region of the branch (`predIndex`), or None to 42 // represent the parent operation, try to return the index into the outputs of 43 // this region predecessor that correspond to the input values of `region`. If 44 // an index could not be found, None is returned instead. 45 auto getOperandIndexIfPred = 46 [&](Optional<unsigned> predIndex) -> Optional<unsigned> { 47 SmallVector<RegionSuccessor, 2> successors; 48 branch.getSuccessorRegions(predIndex, successors); 49 for (RegionSuccessor &successor : successors) { 50 if (successor.getSuccessor() != region) 51 continue; 52 // Check that the successor inputs map to the given input value. 53 ValueRange inputs = successor.getSuccessorInputs(); 54 if (inputs.empty()) { 55 output.push_back(inputValue); 56 break; 57 } 58 unsigned firstInputIndex, lastInputIndex; 59 if (region) { 60 firstInputIndex = inputs[0].cast<BlockArgument>().getArgNumber(); 61 lastInputIndex = inputs.back().cast<BlockArgument>().getArgNumber(); 62 } else { 63 firstInputIndex = inputs[0].cast<OpResult>().getResultNumber(); 64 lastInputIndex = inputs.back().cast<OpResult>().getResultNumber(); 65 } 66 if (firstInputIndex > inputIndex || lastInputIndex < inputIndex) { 67 output.push_back(inputValue); 68 break; 69 } 70 return inputIndex - firstInputIndex; 71 } 72 return llvm::None; 73 }; 74 75 // Check branches from the parent operation. 76 if (region) { 77 if (Optional<unsigned> operandIndex = 78 getOperandIndexIfPred(/*predIndex=*/llvm::None)) { 79 collectUnderlyingAddressValues( 80 branch.getSuccessorEntryOperands( 81 region->getRegionNumber())[*operandIndex], 82 maxDepth, visited, output); 83 } 84 } 85 // Check branches from each child region. 86 Operation *op = branch.getOperation(); 87 for (int i = 0, e = op->getNumRegions(); i != e; ++i) { 88 if (Optional<unsigned> operandIndex = getOperandIndexIfPred(i)) { 89 for (Block &block : op->getRegion(i)) { 90 Operation *term = block.getTerminator(); 91 if (term->hasTrait<OpTrait::ReturnLike>()) { 92 collectUnderlyingAddressValues(term->getOperand(*operandIndex), 93 maxDepth, visited, output); 94 } else if (term->getNumSuccessors()) { 95 // Otherwise, if this terminator may exit the region we can't make 96 // any assumptions about which values get passed. 97 output.push_back(inputValue); 98 return; 99 } 100 } 101 } 102 } 103 } 104 105 /// Given a result, collect all of the underlying values being addressed. 106 static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth, 107 DenseSet<Value> &visited, 108 SmallVectorImpl<Value> &output) { 109 Operation *op = result.getOwner(); 110 111 // If this is a view, unwrap to the source. 112 if (ViewLikeOpInterface view = dyn_cast<ViewLikeOpInterface>(op)) 113 return collectUnderlyingAddressValues(view.getViewSource(), maxDepth, 114 visited, output); 115 // Check to see if we can reason about the control flow of this op. 116 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) { 117 return collectUnderlyingAddressValues(branch, /*region=*/nullptr, result, 118 result.getResultNumber(), maxDepth, 119 visited, output); 120 } 121 122 output.push_back(result); 123 } 124 125 /// Given a block argument, collect all of the underlying values being 126 /// addressed. 127 static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth, 128 DenseSet<Value> &visited, 129 SmallVectorImpl<Value> &output) { 130 Block *block = arg.getOwner(); 131 unsigned argNumber = arg.getArgNumber(); 132 133 // Handle the case of a non-entry block. 134 if (!block->isEntryBlock()) { 135 for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) { 136 auto branch = dyn_cast<BranchOpInterface>((*it)->getTerminator()); 137 if (!branch) { 138 // We can't analyze the control flow, so bail out early. 139 output.push_back(arg); 140 return; 141 } 142 143 // Try to get the operand passed for this argument. 144 unsigned index = it.getSuccessorIndex(); 145 Optional<OperandRange> operands = branch.getSuccessorOperands(index); 146 if (!operands) { 147 // We can't analyze the control flow, so bail out early. 148 output.push_back(arg); 149 return; 150 } 151 collectUnderlyingAddressValues((*operands)[argNumber], maxDepth, visited, 152 output); 153 } 154 return; 155 } 156 157 // Otherwise, check to see if we can reason about the control flow of this op. 158 Region *region = block->getParent(); 159 Operation *op = region->getParentOp(); 160 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) { 161 return collectUnderlyingAddressValues(branch, region, arg, argNumber, 162 maxDepth, visited, output); 163 } 164 165 // We can't reason about the underlying address of this argument. 166 output.push_back(arg); 167 } 168 169 /// Given a value, collect all of the underlying values being addressed. 170 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth, 171 DenseSet<Value> &visited, 172 SmallVectorImpl<Value> &output) { 173 // Check that we don't infinitely recurse. 174 if (!visited.insert(value).second) 175 return; 176 if (maxDepth == 0) { 177 output.push_back(value); 178 return; 179 } 180 --maxDepth; 181 182 if (BlockArgument arg = value.dyn_cast<BlockArgument>()) 183 return collectUnderlyingAddressValues(arg, maxDepth, visited, output); 184 collectUnderlyingAddressValues(value.cast<OpResult>(), maxDepth, visited, 185 output); 186 } 187 188 /// Given a value, collect all of the underlying values being addressed. 189 static void collectUnderlyingAddressValues(Value value, 190 SmallVectorImpl<Value> &output) { 191 DenseSet<Value> visited; 192 collectUnderlyingAddressValues(value, maxUnderlyingValueSearchDepth, visited, 193 output); 194 } 195 196 //===----------------------------------------------------------------------===// 197 // LocalAliasAnalysis 198 //===----------------------------------------------------------------------===// 199 200 /// Given a value, try to get an allocation effect attached to it. If 201 /// successful, `allocEffect` is populated with the effect. If an effect was 202 /// found, `allocScopeOp` is also specified if a parent operation of `value` 203 /// could be identified that bounds the scope of the allocated value; i.e. if 204 /// non-null it specifies the parent operation that the allocation does not 205 /// escape. If no scope is found, `allocScopeOp` is set to nullptr. 206 static LogicalResult 207 getAllocEffectFor(Value value, Optional<MemoryEffects::EffectInstance> &effect, 208 Operation *&allocScopeOp) { 209 // Try to get a memory effect interface for the parent operation. 210 Operation *op; 211 if (BlockArgument arg = value.dyn_cast<BlockArgument>()) 212 op = arg.getOwner()->getParentOp(); 213 else 214 op = value.cast<OpResult>().getOwner(); 215 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op); 216 if (!interface) 217 return failure(); 218 219 // Try to find an allocation effect on the resource. 220 if (!(effect = interface.getEffectOnValue<MemoryEffects::Allocate>(value))) 221 return failure(); 222 223 // If we found an allocation effect, try to find a scope for the allocation. 224 // If the resource of this allocation is automatically scoped, find the parent 225 // operation that bounds the allocation scope. 226 if (llvm::isa<SideEffects::AutomaticAllocationScopeResource>( 227 effect->getResource())) { 228 allocScopeOp = op->getParentWithTrait<OpTrait::AutomaticAllocationScope>(); 229 return success(); 230 } 231 232 // TODO: Here we could look at the users to see if the resource is either 233 // freed on all paths within the region, or is just not captured by anything. 234 allocScopeOp = nullptr; 235 return success(); 236 } 237 238 /// Given the two values, return their aliasing behavior. 239 static AliasResult aliasImpl(Value lhs, Value rhs) { 240 if (lhs == rhs) 241 return AliasResult::MustAlias; 242 Operation *lhsAllocScope = nullptr, *rhsAllocScope = nullptr; 243 Optional<MemoryEffects::EffectInstance> lhsAlloc, rhsAlloc; 244 245 // Handle the case where lhs is a constant. 246 Attribute lhsAttr, rhsAttr; 247 if (matchPattern(lhs, m_Constant(&lhsAttr))) { 248 // TODO: This is overly conservative. Two matching constants don't 249 // necessarily map to the same address. For example, if the two values 250 // correspond to different symbols that both represent a definition. 251 if (matchPattern(rhs, m_Constant(&rhsAttr))) 252 return AliasResult::MayAlias; 253 254 // Try to find an alloc effect on rhs. If an effect was found we can't 255 // alias, otherwise we might. 256 return succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope)) 257 ? AliasResult::NoAlias 258 : AliasResult::MayAlias; 259 } 260 // Handle the case where rhs is a constant. 261 if (matchPattern(rhs, m_Constant(&rhsAttr))) { 262 // Try to find an alloc effect on lhs. If an effect was found we can't 263 // alias, otherwise we might. 264 return succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope)) 265 ? AliasResult::NoAlias 266 : AliasResult::MayAlias; 267 } 268 269 // Otherwise, neither of the values are constant so check to see if either has 270 // an allocation effect. 271 bool lhsHasAlloc = succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope)); 272 bool rhsHasAlloc = succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope)); 273 if (lhsHasAlloc == rhsHasAlloc) { 274 // If both values have an allocation effect we know they don't alias, and if 275 // neither have an effect we can't make an assumptions. 276 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias; 277 } 278 279 // When we reach this point we have one value with a known allocation effect, 280 // and one without. Move the one with the effect to the lhs to make the next 281 // checks simpler. 282 if (rhsHasAlloc) { 283 std::swap(lhs, rhs); 284 lhsAlloc = rhsAlloc; 285 lhsAllocScope = rhsAllocScope; 286 } 287 288 // If the effect has a scoped allocation region, check to see if the 289 // non-effect value is defined above that scope. 290 if (lhsAllocScope) { 291 // If the parent operation of rhs is an ancestor of the allocation scope, or 292 // if rhs is an entry block argument of the allocation scope we know the two 293 // values can't alias. 294 Operation *rhsParentOp = rhs.getParentRegion()->getParentOp(); 295 if (rhsParentOp->isProperAncestor(lhsAllocScope)) 296 return AliasResult::NoAlias; 297 if (rhsParentOp == lhsAllocScope) { 298 BlockArgument rhsArg = rhs.dyn_cast<BlockArgument>(); 299 if (rhsArg && rhs.getParentBlock()->isEntryBlock()) 300 return AliasResult::NoAlias; 301 } 302 } 303 304 // If we couldn't reason about the relationship between the two values, 305 // conservatively assume they might alias. 306 return AliasResult::MayAlias; 307 } 308 309 /// Given the two values, return their aliasing behavior. 310 AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) { 311 if (lhs == rhs) 312 return AliasResult::MustAlias; 313 314 // Get the underlying values being addressed. 315 SmallVector<Value, 8> lhsValues, rhsValues; 316 collectUnderlyingAddressValues(lhs, lhsValues); 317 collectUnderlyingAddressValues(rhs, rhsValues); 318 319 // If we failed to collect for either of the values somehow, conservatively 320 // assume they may alias. 321 if (lhsValues.empty() || rhsValues.empty()) 322 return AliasResult::MayAlias; 323 324 // Check the alias results against each of the underlying values. 325 Optional<AliasResult> result; 326 for (Value lhsVal : lhsValues) { 327 for (Value rhsVal : rhsValues) { 328 AliasResult nextResult = aliasImpl(lhsVal, rhsVal); 329 result = result ? result->merge(nextResult) : nextResult; 330 } 331 } 332 333 // We should always have a valid result here. 334 return *result; 335 } 336