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