MLIR 22.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 = initialSuccessor.getSuccessorInputs();
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 branch, RegionSuccessor(op, op->getResults()), result,
113 result.getResultNumber(), 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 RegionSuccessor regionSuccessor(region);
174 bool found = false;
175 for (RegionSuccessor &successor : successors) {
176 if (successor.getSuccessor() == region) {
177 LDBG() << " Found matching region successor: " << successor;
178 found = true;
179 regionSuccessor = successor;
180 break;
181 }
182 }
183 if (!found) {
184 LDBG()
185 << " No matching region successor found, adding argument to output";
186 output.push_back(arg);
187 return;
188 }
190 branch, regionSuccessor, arg, argNumber, maxDepth, visited, output);
191 }
192
193 LDBG()
194 << " Cannot reason about underlying address, adding argument to output";
195 // We can't reason about the underlying address of this argument.
196 output.push_back(arg);
197}
198
199/// Given a value, collect all of the underlying values being addressed.
200static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
201 DenseSet<Value> &visited,
202 SmallVectorImpl<Value> &output) {
203 LDBG() << "collectUnderlyingAddressValues: " << value;
204 LDBG() << " maxDepth: " << maxDepth;
205
206 // Check that we don't infinitely recurse.
207 if (!visited.insert(value).second) {
208 LDBG() << " Value already visited, skipping";
209 return;
210 }
211 if (maxDepth == 0) {
212 LDBG() << " Max depth reached, adding value to output";
213 output.push_back(value);
214 return;
215 }
216 --maxDepth;
217
218 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
219 LDBG() << " Processing as BlockArgument";
220 return collectUnderlyingAddressValues(arg, maxDepth, visited, output);
221 }
222 LDBG() << " Processing as OpResult";
223 collectUnderlyingAddressValues(cast<OpResult>(value), maxDepth, visited,
224 output);
225}
226
227/// Given a value, collect all of the underlying values being addressed.
229 SmallVectorImpl<Value> &output) {
230 LDBG() << "collectUnderlyingAddressValues: " << value;
231 DenseSet<Value> visited;
233 output);
234 LDBG() << " Collected " << output.size() << " underlying values";
235}
236
237//===----------------------------------------------------------------------===//
238// LocalAliasAnalysis: alias
239//===----------------------------------------------------------------------===//
240
241/// Given a value, try to get an allocation effect attached to it. If
242/// successful, `allocEffect` is populated with the effect. If an effect was
243/// found, `allocScopeOp` is also specified if a parent operation of `value`
244/// could be identified that bounds the scope of the allocated value; i.e. if
245/// non-null it specifies the parent operation that the allocation does not
246/// escape. If no scope is found, `allocScopeOp` is set to nullptr.
247static LogicalResult
249 std::optional<MemoryEffects::EffectInstance> &effect,
250 Operation *&allocScopeOp) {
251 LDBG() << "getAllocEffectFor: " << value;
252
253 // Try to get a memory effect interface for the parent operation.
254 Operation *op;
255 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
256 op = arg.getOwner()->getParentOp();
257 LDBG() << " BlockArgument, parent op: "
258 << OpWithFlags(op, OpPrintingFlags().skipRegions());
259 } else {
260 op = cast<OpResult>(value).getOwner();
261 LDBG() << " OpResult, owner op: "
262 << OpWithFlags(op, OpPrintingFlags().skipRegions());
263 }
264
265 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
266 if (!interface) {
267 LDBG() << " No memory effect interface found";
268 return failure();
269 }
270
271 // Try to find an allocation effect on the resource.
272 if (!(effect = interface.getEffectOnValue<MemoryEffects::Allocate>(value))) {
273 LDBG() << " No allocation effect found on value";
274 return failure();
275 }
276
277 LDBG() << " Found allocation effect";
278
279 // If we found an allocation effect, try to find a scope for the allocation.
280 // If the resource of this allocation is automatically scoped, find the parent
281 // operation that bounds the allocation scope.
282 if (llvm::isa<SideEffects::AutomaticAllocationScopeResource>(
283 effect->getResource())) {
284 allocScopeOp = op->getParentWithTrait<OpTrait::AutomaticAllocationScope>();
285 if (allocScopeOp) {
286 LDBG() << " Automatic allocation scope found: "
287 << OpWithFlags(allocScopeOp, OpPrintingFlags().skipRegions());
288 } else {
289 LDBG() << " Automatic allocation scope found: null";
290 }
291 return success();
292 }
293
294 // TODO: Here we could look at the users to see if the resource is either
295 // freed on all paths within the region, or is just not captured by anything.
296 // For now assume allocation scope to the function scope (we don't care if
297 // pointer escape outside function).
298 allocScopeOp = op->getParentOfType<FunctionOpInterface>();
299 if (allocScopeOp) {
300 LDBG() << " Function scope found: "
301 << OpWithFlags(allocScopeOp, OpPrintingFlags().skipRegions());
302 } else {
303 LDBG() << " Function scope found: null";
304 }
305 return success();
306}
307
310 return op;
311
312 return nullptr;
313}
314
316 unsigned argNumber = cast<OpResult>(value).getResultNumber();
317 return op->getOperand(argNumber);
318}
319
320static std::optional<AliasResult> checkDistinctObjects(Value lhs, Value rhs) {
321 // We should already checked that lhs and rhs are different.
322 assert(lhs != rhs && "lhs and rhs must be different");
323
324 // Result and corresponding operand must alias.
325 auto lhsOp = isDistinctObjectsOp(lhs.getDefiningOp());
326 if (lhsOp && getDistinctObjectsOperand(lhsOp, lhs) == rhs)
328
329 auto rhsOp = isDistinctObjectsOp(rhs.getDefiningOp());
330 if (rhsOp && getDistinctObjectsOperand(rhsOp, rhs) == lhs)
332
333 // If two different values come from the same `DistinctObjects` operation,
334 // they don't alias.
335 if (lhsOp && lhsOp == rhsOp)
337
338 return std::nullopt;
339}
340
341/// Given the two values, return their aliasing behavior.
343 LDBG() << "aliasImpl: " << lhs << " vs " << rhs;
344
345 if (lhs == rhs) {
346 LDBG() << " Same value, must alias";
348 }
349
350 Operation *lhsAllocScope = nullptr, *rhsAllocScope = nullptr;
351 std::optional<MemoryEffects::EffectInstance> lhsAlloc, rhsAlloc;
352
353 // Handle the case where lhs is a constant.
354 Attribute lhsAttr, rhsAttr;
355 if (matchPattern(lhs, m_Constant(&lhsAttr))) {
356 LDBG() << " lhs is constant";
357 // TODO: This is overly conservative. Two matching constants don't
358 // necessarily map to the same address. For example, if the two values
359 // correspond to different symbols that both represent a definition.
360 if (matchPattern(rhs, m_Constant(&rhsAttr))) {
361 LDBG() << " rhs is also constant, may alias";
363 }
364
365 // Try to find an alloc effect on rhs. If an effect was found we can't
366 // alias, otherwise we might.
367 bool rhsHasAlloc =
368 succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope));
369 LDBG() << " rhs has alloc effect: " << rhsHasAlloc;
370 return rhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
371 }
372 // Handle the case where rhs is a constant.
373 if (matchPattern(rhs, m_Constant(&rhsAttr))) {
374 LDBG() << " rhs is constant";
375 // Try to find an alloc effect on lhs. If an effect was found we can't
376 // alias, otherwise we might.
377 bool lhsHasAlloc =
378 succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope));
379 LDBG() << " lhs has alloc effect: " << lhsHasAlloc;
380 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
381 }
382
383 if (std::optional<AliasResult> result = checkDistinctObjects(lhs, rhs))
384 return *result;
385
386 // Otherwise, neither of the values are constant so check to see if either has
387 // an allocation effect.
388 bool lhsHasAlloc = succeeded(getAllocEffectFor(lhs, lhsAlloc, lhsAllocScope));
389 bool rhsHasAlloc = succeeded(getAllocEffectFor(rhs, rhsAlloc, rhsAllocScope));
390 LDBG() << " lhs has alloc effect: " << lhsHasAlloc;
391 LDBG() << " rhs has alloc effect: " << rhsHasAlloc;
392
393 if (lhsHasAlloc == rhsHasAlloc) {
394 // If both values have an allocation effect we know they don't alias, and if
395 // neither have an effect we can't make an assumptions.
396 LDBG() << " Both have same alloc status: "
397 << (lhsHasAlloc ? "NoAlias" : "MayAlias");
398 return lhsHasAlloc ? AliasResult::NoAlias : AliasResult::MayAlias;
399 }
400
401 // When we reach this point we have one value with a known allocation effect,
402 // and one without. Move the one with the effect to the lhs to make the next
403 // checks simpler.
404 if (rhsHasAlloc) {
405 LDBG() << " Swapping lhs and rhs to put alloc effect on lhs";
406 std::swap(lhs, rhs);
407 lhsAlloc = rhsAlloc;
408 lhsAllocScope = rhsAllocScope;
409 }
410
411 // If the effect has a scoped allocation region, check to see if the
412 // non-effect value is defined above that scope.
413 if (lhsAllocScope) {
414 LDBG() << " Checking allocation scope: "
415 << OpWithFlags(lhsAllocScope, OpPrintingFlags().skipRegions());
416 // If the parent operation of rhs is an ancestor of the allocation scope, or
417 // if rhs is an entry block argument of the allocation scope we know the two
418 // values can't alias.
419 Operation *rhsParentOp = rhs.getParentRegion()->getParentOp();
420 if (rhsParentOp->isProperAncestor(lhsAllocScope)) {
421 LDBG() << " rhs parent is ancestor of alloc scope, no alias";
423 }
424 if (rhsParentOp == lhsAllocScope) {
425 BlockArgument rhsArg = dyn_cast<BlockArgument>(rhs);
426 if (rhsArg && rhs.getParentBlock()->isEntryBlock()) {
427 LDBG() << " rhs is entry block arg of alloc scope, no alias";
429 }
430 }
431 }
432
433 // If we couldn't reason about the relationship between the two values,
434 // conservatively assume they might alias.
435 LDBG() << " Cannot reason about relationship, may alias";
437}
438
439/// Given the two values, return their aliasing behavior.
441 LDBG() << "alias: " << lhs << " vs " << rhs;
442
443 if (lhs == rhs) {
444 LDBG() << " Same value, must alias";
446 }
447
448 // Get the underlying values being addressed.
449 SmallVector<Value, 8> lhsValues, rhsValues;
452
453 LDBG() << " lhs underlying values: " << lhsValues.size();
454 LDBG() << " rhs underlying values: " << rhsValues.size();
455
456 // If we failed to collect for either of the values somehow, conservatively
457 // assume they may alias.
458 if (lhsValues.empty() || rhsValues.empty()) {
459 LDBG() << " Failed to collect underlying values, may alias";
461 }
462
463 // Check the alias results against each of the underlying values.
464 std::optional<AliasResult> result;
465 for (Value lhsVal : lhsValues) {
466 for (Value rhsVal : rhsValues) {
467 LDBG() << " Checking underlying values: " << lhsVal << " vs " << rhsVal;
468 AliasResult nextResult = aliasImpl(lhsVal, rhsVal);
469 LDBG() << " Result: "
470 << (nextResult == AliasResult::MustAlias ? "MustAlias"
471 : nextResult == AliasResult::NoAlias ? "NoAlias"
472 : "MayAlias");
473 result = result ? result->merge(nextResult) : nextResult;
474 }
475 }
476
477 // We should always have a valid result here.
478 LDBG() << " Final result: "
479 << (result->isMust() ? "MustAlias"
480 : result->isNo() ? "NoAlias"
481 : "MayAlias");
482 return *result;
483}
484
485//===----------------------------------------------------------------------===//
486// LocalAliasAnalysis: getModRef
487//===----------------------------------------------------------------------===//
488
490 LDBG() << "getModRef: " << OpWithFlags(op, OpPrintingFlags().skipRegions())
491 << " on location " << location;
492
493 // Check to see if this operation relies on nested side effects.
495 LDBG() << " Operation has recursive memory effects, returning ModAndRef";
496 // TODO: To check recursive operations we need to check all of the nested
497 // operations, which can result in a quadratic number of queries. We should
498 // introduce some caching of some kind to help alleviate this, especially as
499 // this caching could be used in other areas of the codebase (e.g. when
500 // checking `wouldOpBeTriviallyDead`).
502 }
503
504 // Otherwise, check to see if this operation has a memory effect interface.
505 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
506 if (!interface) {
507 LDBG() << " No memory effect interface, returning ModAndRef";
509 }
510
511 // Build a ModRefResult by merging the behavior of the effects of this
512 // operation.
514 interface.getEffects(effects);
515 LDBG() << " Found " << effects.size() << " memory effects";
516
518 for (const MemoryEffects::EffectInstance &effect : effects) {
519 if (isa<MemoryEffects::Allocate, MemoryEffects::Free>(effect.getEffect())) {
520 LDBG() << " Skipping alloc/free effect";
521 continue;
522 }
523
524 // Check for an alias between the effect and our memory location.
525 // TODO: Add support for checking an alias with a symbol reference.
527 if (Value effectValue = effect.getValue()) {
528 LDBG() << " Checking alias between effect value " << effectValue
529 << " and location " << location;
530 aliasResult = alias(effectValue, location);
531 LDBG() << " Alias result: "
532 << (aliasResult.isMust() ? "MustAlias"
533 : aliasResult.isNo() ? "NoAlias"
534 : "MayAlias");
535 } else {
536 LDBG() << " No effect value, assuming MayAlias";
537 }
538
539 // If we don't alias, ignore this effect.
540 if (aliasResult.isNo()) {
541 LDBG() << " No alias, ignoring effect";
542 continue;
543 }
544
545 // Merge in the corresponding mod or ref for this effect.
546 if (isa<MemoryEffects::Read>(effect.getEffect())) {
547 LDBG() << " Adding Ref to result";
549 } else {
550 assert(isa<MemoryEffects::Write>(effect.getEffect()));
551 LDBG() << " Adding Mod to result";
553 }
554 if (result.isModAndRef()) {
555 LDBG() << " Result is now ModAndRef, breaking";
556 break;
557 }
558 }
559
560 LDBG() << " Final ModRef result: "
561 << (result.isModAndRef() ? "ModAndRef"
562 : result.isMod() ? "Mod"
563 : result.isRef() ? "Ref"
564 : "NoModRef");
565 return result;
566}
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:236
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:239
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
result_range getResults()
Definition Operation.h:415
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.
ValueRange getSuccessorInputs() const
Return the inputs to the successor that are remapped by the exit values of the current region.
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:128
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.