MLIR 23.0.0git
LocalAliasAnalysis.cpp
Go to the documentation of this file.
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
10
12#include "mlir/IR/Attributes.h"
13#include "mlir/IR/Block.h"
14#include "mlir/IR/Matchers.h"
16#include "mlir/IR/Operation.h"
17#include "mlir/IR/Region.h"
18#include "mlir/IR/Value.h"
23#include "mlir/Support/LLVM.h"
24#include "llvm/Support/Casting.h"
25#include "llvm/Support/DebugLog.h"
26#include <cassert>
27#include <optional>
28#include <utility>
29
30using namespace mlir;
31
32#define DEBUG_TYPE "local-alias-analysis"
33
34//===----------------------------------------------------------------------===//
35// Underlying Address Computation
36//===----------------------------------------------------------------------===//
37
38/// The maximum depth that will be searched when trying to find an underlying
39/// value.
40static constexpr unsigned maxUnderlyingValueSearchDepth = 10;
41
42/// Given a value, collect all of the underlying values being addressed.
43static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
44 DenseSet<Value> &visited,
46
47/// Given a RegionBranchOpInterface operation (`branch`), a Value`inputValue`
48/// which is an input for the provided successor (`initialSuccessor`), try to
49/// find the possible sources for the value along the control flow edges.
51 RegionBranchOpInterface branch, RegionSuccessor initialSuccessor,
52 Value inputValue, unsigned inputIndex, unsigned maxDepth,
53 DenseSet<Value> &visited, SmallVectorImpl<Value> &output) {
54 LDBG() << "collectUnderlyingAddressValues2: "
55 << OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
56 LDBG() << " with initialSuccessor " << initialSuccessor;
57 LDBG() << " inputValue: " << inputValue;
58 LDBG() << " inputIndex: " << inputIndex;
59 LDBG() << " maxDepth: " << maxDepth;
60 ValueRange inputs = branch.getSuccessorInputs(initialSuccessor);
61 if (inputs.empty()) {
62 LDBG() << " input is empty, enqueue value";
63 output.push_back(inputValue);
64 return;
65 }
66 unsigned firstInputIndex, lastInputIndex;
67 if (isa<BlockArgument>(inputs[0])) {
68 firstInputIndex = cast<BlockArgument>(inputs[0]).getArgNumber();
69 lastInputIndex = cast<BlockArgument>(inputs.back()).getArgNumber();
70 } else {
71 firstInputIndex = cast<OpResult>(inputs[0]).getResultNumber();
72 lastInputIndex = cast<OpResult>(inputs.back()).getResultNumber();
73 }
74 if (firstInputIndex > inputIndex || lastInputIndex < inputIndex) {
75 LDBG() << " !! Input index " << inputIndex << " out of range "
76 << firstInputIndex << " to " << lastInputIndex
77 << ", adding input value to output";
78 output.push_back(inputValue);
79 return;
80 }
81 SmallVector<Value> predecessorValues;
82 branch.getPredecessorValues(initialSuccessor, inputIndex - firstInputIndex,
83 predecessorValues);
84 LDBG() << " Found " << predecessorValues.size() << " predecessor values";
85 for (Value predecessorValue : predecessorValues) {
86 LDBG() << " Processing predecessor value: " << predecessorValue;
87 collectUnderlyingAddressValues(predecessorValue, maxDepth, visited, output);
88 }
89}
90
91/// Given a result, collect all of the underlying values being addressed.
92static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
93 DenseSet<Value> &visited,
94 SmallVectorImpl<Value> &output) {
95 LDBG() << "collectUnderlyingAddressValues (OpResult): " << result;
96 LDBG() << " maxDepth: " << maxDepth;
97
98 Operation *op = result.getOwner();
99
100 // If this is a view, unwrap to the source.
101 if (ViewLikeOpInterface view = dyn_cast<ViewLikeOpInterface>(op)) {
102 if (result == view.getViewDest()) {
103 LDBG() << " Unwrapping view to source: " << view.getViewSource();
104 return collectUnderlyingAddressValues(view.getViewSource(), maxDepth,
105 visited, output);
106 }
107 }
108 // Check to see if we can reason about the control flow of this op.
109 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
110 LDBG() << " Processing region branch operation";
112 result, result.getResultNumber(),
113 maxDepth, visited, output);
114 }
115
116 LDBG() << " Adding result to output: " << result;
117 output.push_back(result);
118}
119
120/// Given a block argument, collect all of the underlying values being
121/// addressed.
122static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
123 DenseSet<Value> &visited,
124 SmallVectorImpl<Value> &output) {
125 LDBG() << "collectUnderlyingAddressValues (BlockArgument): " << arg;
126 LDBG() << " maxDepth: " << maxDepth;
127 LDBG() << " argNumber: " << arg.getArgNumber();
128 LDBG() << " isEntryBlock: " << arg.getOwner()->isEntryBlock();
129
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 LDBG() << " Processing non-entry block with "
136 << std::distance(block->pred_begin(), block->pred_end())
137 << " predecessors";
138 for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) {
139 auto branch = dyn_cast<BranchOpInterface>((*it)->getTerminator());
140 if (!branch) {
141 LDBG() << " Cannot analyze control flow, adding argument to output";
142 // We can't analyze the control flow, so bail out early.
143 output.push_back(arg);
144 return;
145 }
146
147 // Try to get the operand passed for this argument.
148 unsigned index = it.getSuccessorIndex();
149 Value operand = branch.getSuccessorOperands(index)[argNumber];
150 if (!operand) {
151 LDBG() << " No operand found for argument, adding to output";
152 // We can't analyze the control flow, so bail out early.
153 output.push_back(arg);
154 return;
155 }
156 LDBG() << " Processing operand from predecessor: " << operand;
157 collectUnderlyingAddressValues(operand, maxDepth, visited, output);
158 }
159 return;
160 }
161
162 // Otherwise, check to see if we can reason about the control flow of this op.
163 Region *region = block->getParent();
164 Operation *op = region->getParentOp();
165 if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
166 LDBG() << " Processing region branch operation for entry block";
167 // We have to find the successor matching the region, so that the input
168 // arguments are correctly set.
169 // TODO: this isn't comprehensive: the successor may not be reachable from
170 // the entry block.
172 branch.getSuccessorRegions(RegionBranchPoint::parent(), successors);
173 for (RegionSuccessor &successor : successors) {
174 if (successor.getSuccessor() == region) {
175 LDBG() << " Found matching region successor: " << successor;
177 branch, successor, arg, argNumber, maxDepth, visited, output);
178 }
179 }
180 LDBG() << " No matching region successor found, adding argument to output";
181 output.push_back(arg);
182 return;
183 }
184
185 LDBG()
186 << " Cannot reason about underlying address, adding argument to output";
187 // We can't reason about the underlying address of this argument.
188 output.push_back(arg);
189}
190
191/// Given a value, collect all of the underlying values being addressed.
192static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
193 DenseSet<Value> &visited,
194 SmallVectorImpl<Value> &output) {
195 LDBG() << "collectUnderlyingAddressValues: " << value;
196 LDBG() << " maxDepth: " << maxDepth;
197
198 // Check that we don't infinitely recurse.
199 if (!visited.insert(value).second) {
200 LDBG() << " Value already visited, skipping";
201 return;
202 }
203 if (maxDepth == 0) {
204 LDBG() << " Max depth reached, adding value to output";
205 output.push_back(value);
206 return;
207 }
208 --maxDepth;
209
210 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
211 LDBG() << " Processing as BlockArgument";
212 return collectUnderlyingAddressValues(arg, maxDepth, visited, output);
213 }
214 LDBG() << " Processing as OpResult";
215 collectUnderlyingAddressValues(cast<OpResult>(value), maxDepth, visited,
216 output);
217}
218
219/// Given a value, collect all of the underlying values being addressed.
221 SmallVectorImpl<Value> &output) {
222 LDBG() << "collectUnderlyingAddressValues: " << value;
223 DenseSet<Value> visited;
225 output);
226 LDBG() << " Collected " << output.size() << " underlying values";
227}
228
229//===----------------------------------------------------------------------===//
230// LocalAliasAnalysis: alias
231//===----------------------------------------------------------------------===//
232
233/// Given a value, try to get an allocation effect attached to it. If
234/// successful, `allocEffect` is populated with the effect. If an effect was
235/// found, `allocScopeOp` is also specified if a parent operation of `value`
236/// could be identified that bounds the scope of the allocated value; i.e. if
237/// non-null it specifies the parent operation that the allocation does not
238/// escape. If no scope is found, `allocScopeOp` is set to nullptr.
239static LogicalResult
241 std::optional<MemoryEffects::EffectInstance> &effect,
242 Operation *&allocScopeOp) {
243 LDBG() << "getAllocEffectFor: " << value;
244
245 // Try to get a memory effect interface for the parent operation.
246 Operation *op;
247 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
248 op = arg.getOwner()->getParentOp();
249 LDBG() << " BlockArgument, parent op: "
250 << OpWithFlags(op, OpPrintingFlags().skipRegions());
251 } else {
252 op = cast<OpResult>(value).getOwner();
253 LDBG() << " OpResult, owner op: "
254 << OpWithFlags(op, OpPrintingFlags().skipRegions());
255 }
256
257 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
258 if (!interface) {
259 LDBG() << " No memory effect interface found";
260 return failure();
261 }
262
263 // Try to find an allocation effect on the resource.
264 if (!(effect = interface.getEffectOnValue<MemoryEffects::Allocate>(value))) {
265 LDBG() << " No allocation effect found on value";
266 return failure();
267 }
268
269 LDBG() << " Found allocation effect";
270
271 // If we found an allocation effect, try to find a scope for the allocation.
272 // If the resource of this allocation is automatically scoped, find the parent
273 // operation that bounds the allocation scope.
274 if (llvm::isa<SideEffects::AutomaticAllocationScopeResource>(
275 effect->getResource())) {
276 allocScopeOp = op->getParentWithTrait<OpTrait::AutomaticAllocationScope>();
277 if (allocScopeOp) {
278 LDBG() << " Automatic allocation scope found: "
279 << OpWithFlags(allocScopeOp, OpPrintingFlags().skipRegions());
280 } else {
281 LDBG() << " Automatic allocation scope found: null";
282 }
283 return success();
284 }
285
286 // TODO: Here we could look at the users to see if the resource is either
287 // freed on all paths within the region, or is just not captured by anything.
288 // For now assume allocation scope to the function scope (we don't care if
289 // pointer escape outside function).
290 allocScopeOp = op->getParentOfType<FunctionOpInterface>();
291 if (allocScopeOp) {
292 LDBG() << " Function scope found: "
293 << OpWithFlags(allocScopeOp, OpPrintingFlags().skipRegions());
294 } else {
295 LDBG() << " Function scope found: null";
296 }
297 return success();
298}
299
302 return op;
303
304 return nullptr;
305}
306
308 unsigned argNumber = cast<OpResult>(value).getResultNumber();
309 return op->getOperand(argNumber);
310}
311
312static std::optional<AliasResult> checkDistinctObjects(Value lhs, Value rhs) {
313 // We should already checked that lhs and rhs are different.
314 assert(lhs != rhs && "lhs and rhs must be different");
315
316 // Result and corresponding operand must alias.
317 auto lhsOp = isDistinctObjectsOp(lhs.getDefiningOp());
318 if (lhsOp && getDistinctObjectsOperand(lhsOp, lhs) == rhs)
320
321 auto rhsOp = isDistinctObjectsOp(rhs.getDefiningOp());
322 if (rhsOp && getDistinctObjectsOperand(rhsOp, rhs) == lhs)
324
325 // If two different values come from the same `DistinctObjects` operation,
326 // they don't alias.
327 if (lhsOp && lhsOp == rhsOp)
329
330 return std::nullopt;
331}
332
333/// Given the two values, return their aliasing behavior.
335 LDBG() << "aliasImpl: " << lhs << " vs " << rhs;
336
337 if (lhs == rhs) {
338 LDBG() << " Same value, must alias";
340 }
341
342 Operation *lhsAllocScope = nullptr, *rhsAllocScope = nullptr;
343 std::optional<MemoryEffects::EffectInstance> lhsAlloc, rhsAlloc;
344
345 // Handle the case where lhs is a constant.
346 Attribute lhsAttr, rhsAttr;
347 if (matchPattern(lhs, m_Constant(&lhsAttr))) {
348 LDBG() << " lhs is constant";
349 // TODO: This is overly conservative. Two matching constants don't
350 // necessarily map to the same address. For example, if the two values
351 // correspond to different symbols that both represent a definition.
352 if (matchPattern(rhs, m_Constant(&rhsAttr))) {
353 LDBG() << " rhs is also constant, may alias";
355 }
356
357 // Try to find an alloc effect on rhs. If an effect was found we can't
358 // alias, otherwise we might.
359 bool rhsHasAlloc =
360 succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope));
361 LDBG() << " rhs has alloc effect: " << rhsHasAlloc;
362 return rhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
363 }
364 // Handle the case where rhs is a constant.
365 if (matchPattern(rhs, m_Constant(&rhsAttr))) {
366 LDBG() << " rhs is constant";
367 // Try to find an alloc effect on lhs. If an effect was found we can't
368 // alias, otherwise we might.
369 bool lhsHasAlloc =
370 succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope));
371 LDBG() << " lhs has alloc effect: " << lhsHasAlloc;
372 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
373 }
374
375 if (std::optional<AliasResult> result = checkDistinctObjects(lhs, rhs))
376 return *result;
377
378 // Otherwise, neither of the values are constant so check to see if either has
379 // an allocation effect.
380 bool lhsHasAlloc = succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope));
381 bool rhsHasAlloc = succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope));
382 LDBG() << " lhs has alloc effect: " << lhsHasAlloc;
383 LDBG() << " rhs has alloc effect: " << rhsHasAlloc;
384
385 if (lhsHasAlloc == rhsHasAlloc) {
386 // If both values have an allocation effect we know they don't alias, and if
387 // neither have an effect we can't make an assumptions.
388 LDBG() << " Both have same alloc status: "
389 << (lhsHasAlloc ? "NoAlias" : "MayAlias");
390 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
391 }
392
393 // When we reach this point we have one value with a known allocation effect,
394 // and one without. Move the one with the effect to the lhs to make the next
395 // checks simpler.
396 if (rhsHasAlloc) {
397 LDBG() << " Swapping lhs and rhs to put alloc effect on lhs";
398 std::swap(lhs, rhs);
399 lhsAlloc = rhsAlloc;
400 lhsAllocScope = rhsAllocScope;
401 }
402
403 // If the effect has a scoped allocation region, check to see if the
404 // non-effect value is defined above that scope.
405 if (lhsAllocScope) {
406 LDBG() << " Checking allocation scope: "
407 << OpWithFlags(lhsAllocScope, OpPrintingFlags().skipRegions());
408 // If the parent operation of rhs is an ancestor of the allocation scope, or
409 // if rhs is an entry block argument of the allocation scope we know the two
410 // values can't alias.
411 Operation *rhsParentOp = rhs.getParentRegion()->getParentOp();
412 if (rhsParentOp->isProperAncestor(lhsAllocScope)) {
413 LDBG() << " rhs parent is ancestor of alloc scope, no alias";
415 }
416 if (rhsParentOp == lhsAllocScope) {
417 BlockArgument rhsArg = dyn_cast<BlockArgument>(rhs);
418 if (rhsArg && rhs.getParentBlock()->isEntryBlock()) {
419 LDBG() << " rhs is entry block arg of alloc scope, no alias";
421 }
422 }
423 }
424
425 // If we couldn't reason about the relationship between the two values,
426 // conservatively assume they might alias.
427 LDBG() << " Cannot reason about relationship, may alias";
429}
430
431/// Given the two values, return their aliasing behavior.
433 LDBG() << "alias: " << lhs << " vs " << rhs;
434
435 if (lhs == rhs) {
436 LDBG() << " Same value, must alias";
438 }
439
440 // Get the underlying values being addressed.
441 SmallVector<Value, 8> lhsValues, rhsValues;
444
445 LDBG() << " lhs underlying values: " << lhsValues.size();
446 LDBG() << " rhs underlying values: " << rhsValues.size();
447
448 // If we failed to collect for either of the values somehow, conservatively
449 // assume they may alias.
450 if (lhsValues.empty() || rhsValues.empty()) {
451 LDBG() << " Failed to collect underlying values, may alias";
453 }
454
455 // Check the alias results against each of the underlying values.
456 std::optional<AliasResult> result;
457 for (Value lhsVal : lhsValues) {
458 for (Value rhsVal : rhsValues) {
459 LDBG() << " Checking underlying values: " << lhsVal << " vs " << rhsVal;
460 AliasResult nextResult = aliasImpl(lhsVal, rhsVal);
461 LDBG() << " Result: "
462 << (nextResult == AliasResult::MustAlias ? "MustAlias"
463 : nextResult == AliasResult::NoAlias ? "NoAlias"
464 : "MayAlias");
465 result = result ? result->merge(nextResult) : nextResult;
466 }
467 }
468
469 // We should always have a valid result here.
470 LDBG() << " Final result: "
471 << (result->isMust() ? "MustAlias"
472 : result->isNo() ? "NoAlias"
473 : "MayAlias");
474 return *result;
475}
476
477//===----------------------------------------------------------------------===//
478// LocalAliasAnalysis: getModRef
479//===----------------------------------------------------------------------===//
480
482 LDBG() << "getModRef: " << OpWithFlags(op, OpPrintingFlags().skipRegions())
483 << " on location " << location;
484
485 // Check to see if this operation relies on nested side effects.
487 LDBG() << " Operation has recursive memory effects, returning ModAndRef";
488 // TODO: To check recursive operations we need to check all of the nested
489 // operations, which can result in a quadratic number of queries. We should
490 // introduce some caching of some kind to help alleviate this, especially as
491 // this caching could be used in other areas of the codebase (e.g. when
492 // checking `wouldOpBeTriviallyDead`).
494 }
495
496 // Otherwise, check to see if this operation has a memory effect interface.
497 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
498 if (!interface) {
499 LDBG() << " No memory effect interface, returning ModAndRef";
501 }
502
503 // Build a ModRefResult by merging the behavior of the effects of this
504 // operation.
506 interface.getEffects(effects);
507 LDBG() << " Found " << effects.size() << " memory effects";
508
510 for (const MemoryEffects::EffectInstance &effect : effects) {
511 if (isa<MemoryEffects::Allocate, MemoryEffects::Free>(effect.getEffect())) {
512 LDBG() << " Skipping alloc/free effect";
513 continue;
514 }
515
516 // Check for an alias between the effect and our memory location.
517 // TODO: Add support for checking an alias with a symbol reference.
519 if (Value effectValue = effect.getValue()) {
520 LDBG() << " Checking alias between effect value " << effectValue
521 << " and location " << location;
522 aliasResult = alias(effectValue, location);
523 LDBG() << " Alias result: "
524 << (aliasResult.isMust() ? "MustAlias"
525 : aliasResult.isNo() ? "NoAlias"
526 : "MayAlias");
527 } else {
528 LDBG() << " No effect value, assuming MayAlias";
529 }
530
531 // If we don't alias, ignore this effect.
532 if (aliasResult.isNo()) {
533 LDBG() << " No alias, ignoring effect";
534 continue;
535 }
536
537 // Merge in the corresponding mod or ref for this effect.
538 if (isa<MemoryEffects::Read>(effect.getEffect())) {
539 LDBG() << " Adding Ref to result";
541 } else {
542 assert(isa<MemoryEffects::Write>(effect.getEffect()));
543 LDBG() << " Adding Mod to result";
545 }
546 if (result.isModAndRef()) {
547 LDBG() << " Result is now ModAndRef, breaking";
548 break;
549 }
550 }
551
552 LDBG() << " Final ModRef result: "
553 << (result.isModAndRef() ? "ModAndRef"
554 : result.isMod() ? "Mod"
555 : result.isRef() ? "Ref"
556 : "NoModRef");
557 return result;
558}
return success()
lhs
static std::optional< AliasResult > checkDistinctObjects(Value lhs, Value rhs)
static LogicalResult getAllocEffectFor(Value value, std::optional< MemoryEffects::EffectInstance > &effect, Operation *&allocScopeOp)
Given a value, try to get an allocation effect attached to it.
static void collectUnderlyingAddressValues(Value value, unsigned maxDepth, DenseSet< Value > &visited, SmallVectorImpl< Value > &output)
Given a value, collect all of the underlying values being addressed.
static constexpr unsigned maxUnderlyingValueSearchDepth
The maximum depth that will be searched when trying to find an underlying value.
static void collectUnderlyingAddressValues2(RegionBranchOpInterface branch, RegionSuccessor initialSuccessor, Value inputValue, unsigned inputIndex, unsigned maxDepth, DenseSet< Value > &visited, SmallVectorImpl< Value > &output)
Given a RegionBranchOpInterface operation (branch), a ValueinputValue which is an input for the provi...
static Value getDistinctObjectsOperand(Operation *op, Value value)
static Operation * isDistinctObjectsOp(Operation *op)
The possible results of an alias query.
bool isMust() const
Returns if this result is a must alias.
bool isNo() const
Returns if this result indicates no possibility of aliasing.
@ MustAlias
The two locations precisely alias each other.
@ MayAlias
The two locations may or may not alias.
@ NoAlias
The two locations do not alias at all.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:309
unsigned getArgNumber() const
Returns the number of this argument.
Definition Value.h:321
Block * getOwner() const
Returns the block that owns this argument.
Definition Value.h:318
Block represents an ordered list of Operations.
Definition Block.h:33
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
pred_iterator pred_begin()
Definition Block.h:246
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition Block.cpp:36
pred_iterator pred_end()
Definition Block.h:249
ModRefResult getModRef(Operation *op, Value location)
Return the modify-reference behavior of op on location.
virtual AliasResult aliasImpl(Value lhs, Value rhs)
Given the two values, return their aliasing behavior.
AliasResult alias(Value lhs, Value rhs)
Given two values, return their aliasing behavior.
The possible results of whether a memory access modifies or references a memory location.
static ModRefResult getRef()
Return a new result that indicates that the memory access may reference the value stored in memory.
static ModRefResult getNoModRef()
Return a new result that indicates that the memory access neither references nor modifies the value s...
static ModRefResult getModAndRef()
Return a new result that indicates that the memory access may reference and may modify the value stor...
static ModRefResult getMod()
Return a new result that indicates that the memory access may modify the value stored in memory.
Set of flags used to control the behavior of the various IR print methods (e.g.
This is a value defined by a result of an operation.
Definition Value.h:457
This trai indicates that pointer-like objects (such as memrefs) returned from this operation will nev...
This trait indicates that the memory effects of an operation includes the effects of operations neste...
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition Operation.h:1111
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Value getOperand(unsigned idx)
Definition Operation.h:350
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:749
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class represents a successor of a region.
static RegionSuccessor parent()
Initialize a successor that branches after/out of the parent operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:200
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
SideEffects::EffectInstance< Effect > EffectInstance
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:120
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
The following effect indicates that the operation allocates from some resource.