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/FunctionInterfaces.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.
collectUnderlyingAddressValues(RegionBranchOpInterface branch,Region * region,Value inputValue,unsigned inputIndex,unsigned maxDepth,DenseSet<Value> & visited,SmallVectorImpl<Value> & output)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 }
82 if (Optional<unsigned> operandIndex =
83 getOperandIndexIfPred(/*predIndex=*/llvm::None)) {
84 collectUnderlyingAddressValues(
85 branch.getSuccessorEntryOperands(regionIndex)[*operandIndex], maxDepth,
86 visited, output);
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.
collectUnderlyingAddressValues(OpResult result,unsigned maxDepth,DenseSet<Value> & visited,SmallVectorImpl<Value> & output)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.
collectUnderlyingAddressValues(BlockArgument arg,unsigned maxDepth,DenseSet<Value> & visited,SmallVectorImpl<Value> & output)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 Value operand = branch.getSuccessorOperands(index)[argNumber];
153 if (!operand) {
154 // We can't analyze the control flow, so bail out early.
155 output.push_back(arg);
156 return;
157 }
158 collectUnderlyingAddressValues(operand, maxDepth, visited, output);
159 }
160 return;
161 }
162
163 // Otherwise, check to see if we can reason about the control flow of this op.
164 Region *region = block->getParent();
165 Operation *op = region->getParentOp();
166 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
167 return collectUnderlyingAddressValues(branch, region, arg, argNumber,
168 maxDepth, visited, output);
169 }
170
171 // We can't reason about the underlying address of this argument.
172 output.push_back(arg);
173 }
174
175 /// Given a value, collect all of the underlying values being addressed.
collectUnderlyingAddressValues(Value value,unsigned maxDepth,DenseSet<Value> & visited,SmallVectorImpl<Value> & output)176 static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
177 DenseSet<Value> &visited,
178 SmallVectorImpl<Value> &output) {
179 // Check that we don't infinitely recurse.
180 if (!visited.insert(value).second)
181 return;
182 if (maxDepth == 0) {
183 output.push_back(value);
184 return;
185 }
186 --maxDepth;
187
188 if (BlockArgument arg = value.dyn_cast<BlockArgument>())
189 return collectUnderlyingAddressValues(arg, maxDepth, visited, output);
190 collectUnderlyingAddressValues(value.cast<OpResult>(), maxDepth, visited,
191 output);
192 }
193
194 /// Given a value, collect all of the underlying values being addressed.
collectUnderlyingAddressValues(Value value,SmallVectorImpl<Value> & output)195 static void collectUnderlyingAddressValues(Value value,
196 SmallVectorImpl<Value> &output) {
197 DenseSet<Value> visited;
198 collectUnderlyingAddressValues(value, maxUnderlyingValueSearchDepth, visited,
199 output);
200 }
201
202 //===----------------------------------------------------------------------===//
203 // LocalAliasAnalysis: alias
204 //===----------------------------------------------------------------------===//
205
206 /// Given a value, try to get an allocation effect attached to it. If
207 /// successful, `allocEffect` is populated with the effect. If an effect was
208 /// found, `allocScopeOp` is also specified if a parent operation of `value`
209 /// could be identified that bounds the scope of the allocated value; i.e. if
210 /// non-null it specifies the parent operation that the allocation does not
211 /// escape. If no scope is found, `allocScopeOp` is set to nullptr.
212 static LogicalResult
getAllocEffectFor(Value value,Optional<MemoryEffects::EffectInstance> & effect,Operation * & allocScopeOp)213 getAllocEffectFor(Value value, Optional<MemoryEffects::EffectInstance> &effect,
214 Operation *&allocScopeOp) {
215 // Try to get a memory effect interface for the parent operation.
216 Operation *op;
217 if (BlockArgument arg = value.dyn_cast<BlockArgument>())
218 op = arg.getOwner()->getParentOp();
219 else
220 op = value.cast<OpResult>().getOwner();
221 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
222 if (!interface)
223 return failure();
224
225 // Try to find an allocation effect on the resource.
226 if (!(effect = interface.getEffectOnValue<MemoryEffects::Allocate>(value)))
227 return failure();
228
229 // If we found an allocation effect, try to find a scope for the allocation.
230 // If the resource of this allocation is automatically scoped, find the parent
231 // operation that bounds the allocation scope.
232 if (llvm::isa<SideEffects::AutomaticAllocationScopeResource>(
233 effect->getResource())) {
234 allocScopeOp = op->getParentWithTrait<OpTrait::AutomaticAllocationScope>();
235 return success();
236 }
237
238 // TODO: Here we could look at the users to see if the resource is either
239 // freed on all paths within the region, or is just not captured by anything.
240 // For now assume allocation scope to the function scope (we don't care if
241 // pointer escape outside function).
242 allocScopeOp = op->getParentOfType<FunctionOpInterface>();
243 return success();
244 }
245
246 /// Given the two values, return their aliasing behavior.
aliasImpl(Value lhs,Value rhs)247 static AliasResult aliasImpl(Value lhs, Value rhs) {
248 if (lhs == rhs)
249 return AliasResult::MustAlias;
250 Operation *lhsAllocScope = nullptr, *rhsAllocScope = nullptr;
251 Optional<MemoryEffects::EffectInstance> lhsAlloc, rhsAlloc;
252
253 // Handle the case where lhs is a constant.
254 Attribute lhsAttr, rhsAttr;
255 if (matchPattern(lhs, m_Constant(&lhsAttr))) {
256 // TODO: This is overly conservative. Two matching constants don't
257 // necessarily map to the same address. For example, if the two values
258 // correspond to different symbols that both represent a definition.
259 if (matchPattern(rhs, m_Constant(&rhsAttr)))
260 return AliasResult::MayAlias;
261
262 // Try to find an alloc effect on rhs. If an effect was found we can't
263 // alias, otherwise we might.
264 return succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope))
265 ? AliasResult::NoAlias
266 : AliasResult::MayAlias;
267 }
268 // Handle the case where rhs is a constant.
269 if (matchPattern(rhs, m_Constant(&rhsAttr))) {
270 // Try to find an alloc effect on lhs. If an effect was found we can't
271 // alias, otherwise we might.
272 return succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope))
273 ? AliasResult::NoAlias
274 : AliasResult::MayAlias;
275 }
276
277 // Otherwise, neither of the values are constant so check to see if either has
278 // an allocation effect.
279 bool lhsHasAlloc = succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope));
280 bool rhsHasAlloc = succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope));
281 if (lhsHasAlloc == rhsHasAlloc) {
282 // If both values have an allocation effect we know they don't alias, and if
283 // neither have an effect we can't make an assumptions.
284 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
285 }
286
287 // When we reach this point we have one value with a known allocation effect,
288 // and one without. Move the one with the effect to the lhs to make the next
289 // checks simpler.
290 if (rhsHasAlloc) {
291 std::swap(lhs, rhs);
292 lhsAlloc = rhsAlloc;
293 lhsAllocScope = rhsAllocScope;
294 }
295
296 // If the effect has a scoped allocation region, check to see if the
297 // non-effect value is defined above that scope.
298 if (lhsAllocScope) {
299 // If the parent operation of rhs is an ancestor of the allocation scope, or
300 // if rhs is an entry block argument of the allocation scope we know the two
301 // values can't alias.
302 Operation *rhsParentOp = rhs.getParentRegion()->getParentOp();
303 if (rhsParentOp->isProperAncestor(lhsAllocScope))
304 return AliasResult::NoAlias;
305 if (rhsParentOp == lhsAllocScope) {
306 BlockArgument rhsArg = rhs.dyn_cast<BlockArgument>();
307 if (rhsArg && rhs.getParentBlock()->isEntryBlock())
308 return AliasResult::NoAlias;
309 }
310 }
311
312 // If we couldn't reason about the relationship between the two values,
313 // conservatively assume they might alias.
314 return AliasResult::MayAlias;
315 }
316
317 /// Given the two values, return their aliasing behavior.
alias(Value lhs,Value rhs)318 AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
319 if (lhs == rhs)
320 return AliasResult::MustAlias;
321
322 // Get the underlying values being addressed.
323 SmallVector<Value, 8> lhsValues, rhsValues;
324 collectUnderlyingAddressValues(lhs, lhsValues);
325 collectUnderlyingAddressValues(rhs, rhsValues);
326
327 // If we failed to collect for either of the values somehow, conservatively
328 // assume they may alias.
329 if (lhsValues.empty() || rhsValues.empty())
330 return AliasResult::MayAlias;
331
332 // Check the alias results against each of the underlying values.
333 Optional<AliasResult> result;
334 for (Value lhsVal : lhsValues) {
335 for (Value rhsVal : rhsValues) {
336 AliasResult nextResult = aliasImpl(lhsVal, rhsVal);
337 result = result ? result->merge(nextResult) : nextResult;
338 }
339 }
340
341 // We should always have a valid result here.
342 return *result;
343 }
344
345 //===----------------------------------------------------------------------===//
346 // LocalAliasAnalysis: getModRef
347 //===----------------------------------------------------------------------===//
348
getModRef(Operation * op,Value location)349 ModRefResult LocalAliasAnalysis::getModRef(Operation *op, Value location) {
350 // Check to see if this operation relies on nested side effects.
351 if (op->hasTrait<OpTrait::HasRecursiveSideEffects>()) {
352 // TODO: To check recursive operations we need to check all of the nested
353 // operations, which can result in a quadratic number of queries. We should
354 // introduce some caching of some kind to help alleviate this, especially as
355 // this caching could be used in other areas of the codebase (e.g. when
356 // checking `wouldOpBeTriviallyDead`).
357 return ModRefResult::getModAndRef();
358 }
359
360 // Otherwise, check to see if this operation has a memory effect interface.
361 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
362 if (!interface)
363 return ModRefResult::getModAndRef();
364
365 // Build a ModRefResult by merging the behavior of the effects of this
366 // operation.
367 SmallVector<MemoryEffects::EffectInstance> effects;
368 interface.getEffects(effects);
369
370 ModRefResult result = ModRefResult::getNoModRef();
371 for (const MemoryEffects::EffectInstance &effect : effects) {
372 if (isa<MemoryEffects::Allocate, MemoryEffects::Free>(effect.getEffect()))
373 continue;
374
375 // Check for an alias between the effect and our memory location.
376 // TODO: Add support for checking an alias with a symbol reference.
377 AliasResult aliasResult = AliasResult::MayAlias;
378 if (Value effectValue = effect.getValue())
379 aliasResult = alias(effectValue, location);
380
381 // If we don't alias, ignore this effect.
382 if (aliasResult.isNo())
383 continue;
384
385 // Merge in the corresponding mod or ref for this effect.
386 if (isa<MemoryEffects::Read>(effect.getEffect())) {
387 result = result.merge(ModRefResult::getRef());
388 } else {
389 assert(isa<MemoryEffects::Write>(effect.getEffect()));
390 result = result.merge(ModRefResult::getMod());
391 }
392 if (result.isModAndRef())
393 break;
394 }
395 return result;
396 }
397