MLIR 24.0.0git
SideEffectInterfaces.cpp
Go to the documentation of this file.
1//===- SideEffectInterfaces.cpp - SideEffects in 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
11#include "mlir/IR/SymbolTable.h"
12#include <utility>
13
14using namespace mlir;
15
16//===----------------------------------------------------------------------===//
17// SideEffect Interfaces
18//===----------------------------------------------------------------------===//
19
20/// Include the definitions of the side effect interfaces.
21#include "mlir/Interfaces/SideEffectInterfaces.cpp.inc"
22
23//===----------------------------------------------------------------------===//
24// MemoryEffects
25//===----------------------------------------------------------------------===//
26
28 return isa<Allocate, Free, Read, Write>(effect);
29}
30
31//===----------------------------------------------------------------------===//
32// SideEffect Utilities
33//===----------------------------------------------------------------------===//
34
36 return op->use_empty() && wouldOpBeTriviallyDead(op);
37}
38
39/// Return true if the op's own memory effects do not prevent erasure. Allowed
40/// effects are reads and effects on results allocated by the op itself.
41///
42/// If the op only has recursive effects, treat the op itself as having no
43/// effects. Without a memory effect interface or recursive effects, return
44/// false to conservatively assume live effects.
45///
46/// The caller must also check nested effects when `hasRecursiveEffects` is
47/// true. This function does not check uses or restrictions on erasing
48/// terminators and symbols.
49static bool hasOnlyRemovableEffects(Operation *op, bool hasRecursiveEffects) {
50 if (auto effectInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
52 effectInterface.getEffects(effects);
53
54 // Gather all results of this op that are allocated.
55 SmallPtrSet<Value, 4> allocResults;
56 for (const MemoryEffects::EffectInstance &effect : effects)
57 if (isa<MemoryEffects::Allocate>(effect.getEffect()) &&
58 effect.getValue() && effect.getValue().getDefiningOp() == op)
59 allocResults.insert(effect.getValue());
60 return llvm::all_of(effects,
61 [&](const MemoryEffects::EffectInstance &effect) {
62 // We can drop effects on values allocated by this
63 // operation and returned as results. Otherwise, the
64 // effect must be a read.
65 return allocResults.contains(effect.getValue()) ||
66 isa<MemoryEffects::Read>(effect.getEffect());
67 });
68 }
69 return hasRecursiveEffects;
70}
71
72/// Internal implementation of `mlir::wouldOpBeTriviallyDead` that also
73/// considers terminator operations as dead if they have no side effects. This
74/// allows for marking region operations as trivially dead without always being
75/// conservative of terminators.
77 Operation *root,
79 using State = DeadnessCache::State;
80 // Repeated queries on nested operations would otherwise scan the same
81 // subtrees. Cache each subtree after checking its effects, and skip cached
82 // subtrees on later queries. An explicit worklist also bounds C++ stack use
83 // for deeply nested IR. Uncached queries do not look up or record summaries.
84 struct WorkItem {
85 // Keep the unvisited range so a block can resume after a nested operation
86 // without adding every sibling to the worklist.
87 Block::iterator current;
89 Operation *complete = nullptr;
90 };
91 SmallVector<WorkItem, 1> worklist = {
92 {Block::iterator(root), ++Block::iterator(root)}};
93 while (!worklist.empty()) {
94 WorkItem &item = worklist.back();
95 if (item.current == item.end) {
96 if (cache && item.complete)
97 (*cache)[item.complete] = State::Dead;
98 worklist.pop_back();
99 continue;
100 }
101 Operation *op = &*item.current++;
102 bool cachedAlive = false;
103 if (cache) {
104 auto cached = cache->find(op);
105 if (cached != cache->end()) {
106 if (cached->second == State::Dead)
107 continue;
108 cachedAlive = true;
109 }
110 }
111 bool recursive = op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
112 if (cachedAlive || !hasOnlyRemovableEffects(op, recursive)) {
113 // One live effect keeps every ancestor up to the root live. The
114 // remaining subtrees cannot change the answer.
115 if (cache) {
116 for (Operation *ancestor = op;; ancestor = ancestor->getParentOp()) {
117 (*cache)[ancestor] = State::Alive;
118 if (ancestor == root)
119 break;
120 }
121 }
122 return false;
123 }
124 if (!recursive) {
125 if (cache)
126 (*cache)[op] = State::Dead;
127 continue;
128 }
129 // The first pushed block is processed last. Record the parent's summary
130 // only when that block is complete and all other blocks have been checked.
131 Operation *complete = cache ? op : nullptr;
132 for (Region &region : op->getRegions()) {
133 for (Block &block : region) {
134 worklist.push_back({block.begin(), block.end(), complete});
135 complete = nullptr;
136 }
137 }
138 if (complete)
139 (*cache)[complete] = State::Dead;
140 }
141 return true;
142}
143
145 // Cache only effects. Uses can change without a change to the defining op.
146 // Nested operations may have internal uses or be terminators even when the
147 // whole root can be erased, so these checks apply only to the root.
148 if (!root->use_empty() || root->mightHaveTrait<OpTrait::IsTerminator>() ||
149 isa<SymbolOpInterface>(root))
150 return false;
151
152 auto cachedRoot = cache.find(root);
153 if (cachedRoot != cache.end())
154 return cachedRoot->second == State::Dead;
155
156 return wouldOpBeTriviallyDeadImpl(root, &cache);
157}
158
160 op->walk([&](Operation *nested) { cache.erase(nested); });
162}
163
165 // An ancestor may report nested effects through its own effect interface,
166 // without querying the intermediate cache entries. A missing entry cannot
167 // stop invalidation of its ancestors.
168 for (Operation *parent = op; parent; parent = parent->getParentOp())
169 cache.erase(parent);
170}
171
172template <typename EffectTy>
174 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
175 if (!memOp)
176 return false;
178 memOp.getEffects(effects);
179 bool hasSingleEffectOnVal = false;
180 // Iterate through `effects` and check if an effect of type `EffectTy` and
181 // only of that type is present.
182 for (auto &effect : effects) {
183 hasSingleEffectOnVal = isa<EffectTy>(effect.getEffect());
184 if (!hasSingleEffectOnVal)
185 return false;
186 }
187 return hasSingleEffectOnVal;
188}
193
194template <typename EffectTy>
196 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
197 if (!memOp)
198 return false;
200 memOp.getEffects(effects);
201 bool hasSingleEffectOnVal = false;
202 // Iterate through `effects` and check if an effect of type `EffectTy` and
203 // only of that type is present.
204 for (auto &effect : effects) {
205 if (effect.getValue() != value)
206 continue;
207 hasSingleEffectOnVal = isa<EffectTy>(effect.getEffect());
208 if (!hasSingleEffectOnVal)
209 return false;
210 }
211 return hasSingleEffectOnVal;
212}
213
215 Value value);
217 Value value);
219 Value value);
221 Value value);
222
223template <typename ValueTy, typename EffectTy>
224bool mlir::hasSingleEffect(Operation *op, ValueTy value) {
225 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
226 if (!memOp)
227 return false;
229 memOp.getEffects(effects);
230 bool hasSingleEffectOnVal = false;
231 // Iterate through `effects` and check if an effect of type `EffectTy` and
232 // only of that type is present on value.
233 for (auto &effect : effects) {
234 if (effect.getEffectValue<ValueTy>() != value)
235 continue;
236 hasSingleEffectOnVal = isa<EffectTy>(effect.getEffect());
237 if (!hasSingleEffectOnVal)
238 return false;
239 }
240 return hasSingleEffectOnVal;
241}
242
243template bool
245 OpOperand *);
246template bool
248 OpOperand *);
249template bool
251 OpOperand *);
252template bool
254 OpOperand *);
255template bool
258 OpResult);
260 OpResult);
262 OpResult);
263template bool
266template bool
269template bool
272template bool
275
276template <typename... EffectTys>
278 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
279 if (!memOp)
280 return false;
282 memOp.getEffects(effects);
283 return llvm::any_of(effects, [&](MemoryEffects::EffectInstance &effect) {
284 return isa<EffectTys...>(effect.getEffect());
285 });
286}
291template bool
293
294template <typename... EffectTys>
296 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
297 if (!memOp)
298 return false;
300 memOp.getEffects(effects);
301 return llvm::any_of(effects, [&](MemoryEffects::EffectInstance &effect) {
302 if (effect.getValue() != value)
303 return false;
304 return isa<EffectTys...>(effect.getEffect());
305 });
306}
308 Value value);
312template bool
314 Value value);
315
316template <typename ValueTy, typename... EffectTys>
317bool mlir::hasEffect(Operation *op, ValueTy value) {
318 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
319 if (!memOp)
320 return false;
322 memOp.getEffects(effects);
323 return llvm::any_of(effects, [&](MemoryEffects::EffectInstance &effect) {
324 if (effect.getEffectValue<ValueTy>() != value)
325 return false;
326 return isa<EffectTys...>(effect.getEffect());
327 });
328}
329template bool
332 OpOperand *);
334 OpOperand *);
336 OpOperand *);
337template bool
339 Operation *, OpOperand *);
340
342 OpResult);
344 OpResult);
346 OpResult);
348 OpResult);
349template bool
352
353template bool
356template bool
358template bool
360template bool
363template bool
366
368 return !isa<MemoryEffectOpInterface>(op) &&
370}
371
374 return false;
375 if (isa<SymbolOpInterface>(op))
376 return false;
378}
379
381 if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
382 if (!memInterface.hasNoEffect())
383 return false;
384 // If the op does not have recursive side effects, then it is memory effect
385 // free.
387 return true;
388 } else if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
389 // Otherwise, if the op does not implement the memory effect interface and
390 // it does not have recursive side effects, then it cannot be known that the
391 // op is moveable.
392 return false;
393 }
394
395 // Recurse into the regions and ensure that all nested ops are memory effect
396 // free.
397 for (Region &region : op->getRegions())
398 for (Operation &op : region.getOps())
399 if (!isMemoryEffectFree(&op))
400 return false;
401 return true;
402}
403
404// the returned vector may contain duplicate effects
405std::optional<llvm::SmallVector<MemoryEffects::EffectInstance>>
408 SmallVector<Operation *> effectingOps(1, rootOp);
409 while (!effectingOps.empty()) {
410 Operation *op = effectingOps.pop_back_val();
411
412 // If the operation has recursive effects, push all of the nested
413 // operations on to the stack to consider.
414 bool hasRecursiveEffects =
416 if (hasRecursiveEffects) {
417 for (Region &region : op->getRegions()) {
418 for (Block &block : region) {
419 for (Operation &nestedOp : block) {
420 effectingOps.push_back(&nestedOp);
421 }
422 }
423 }
424 }
425
426 if (auto effectInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
427 effectInterface.getEffects(effects);
428 } else if (!hasRecursiveEffects) {
429 // the operation does not have recursive memory effects or implement
430 // the memory effect op interface. Its effects are unknown.
431 return std::nullopt;
432 }
433 }
434 return effects;
435}
436
438 auto conditionallySpeculatable = dyn_cast<ConditionallySpeculatable>(op);
439 if (!conditionallySpeculatable)
440 return false;
441
442 switch (conditionallySpeculatable.getSpeculatability()) {
444 for (Region &region : op->getRegions()) {
445 for (Operation &op : region.getOps())
446 if (!isSpeculatable(&op))
447 return false;
448 }
449 return true;
450
452 return true;
453
455 return false;
456 }
457
458 llvm_unreachable("Unhandled enum in mlir::isSpeculatable!");
459}
460
461/// The implementation of this function replicates the `def Pure : TraitList`
462/// in `SideEffectInterfaces.td` and has to be kept in sync manually.
464 return isSpeculatable(op) && isMemoryEffectFree(op);
465}
static bool hasOnlyRemovableEffects(Operation *op, bool hasRecursiveEffects)
Return true if the op's own memory effects do not prevent erasure.
static bool wouldOpBeTriviallyDeadImpl(Operation *root, DenseMap< Operation *, DeadnessCache::State > *cache=nullptr)
Internal implementation of mlir::wouldOpBeTriviallyDead that also considers terminator operations as ...
This class represents an argument of a Block.
Definition Value.h:308
Block represents an ordered list of Operations.
Definition Block.h:34
OpListType::iterator iterator
Definition Block.h:165
bool isOpTriviallyDead(Operation *op)
Return true if the operation is unused and can be erased.
void invalidateAncestors(Operation *op)
Invalidate the operation and its ancestors, preserving descendant entries.
void invalidate(Operation *op)
Invalidate the operation, its descendants, and its ancestors.
This class represents an operand of an operation.
Definition Value.h:256
This is a value defined by a result of an operation.
Definition Value.h:456
This trait indicates that the memory effects of an operation includes the effects of operations neste...
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool use_empty()
Returns true if this operation has no uses.
Definition Operation.h:906
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:803
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Definition Operation.h:811
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:731
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:851
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
EffectT * getEffect() const
Return the effect being applied.
T getEffectValue() const
Returns the OpOperand effect is applied on, or nullptr if there isn't a known value being effected.
Value getValue() const
Return the value the effect is applied on, or nullptr if there isn't a known value being affected.
This class represents a base class for a specific effect type.
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
constexpr auto RecursivelySpeculatable
constexpr auto Speculatable
constexpr auto NotSpeculatable
Include the generated interface declarations.
bool hasSingleEffect(Operation *op)
Returns "true" if op has only an effect of type EffectTy.
bool isPure(Operation *op)
Returns true if the given operation is pure, i.e., is speculatable that does not touch memory.
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
bool wouldOpBeTriviallyDead(Operation *op)
Return true if the given operation would be dead if unused, and has no side effects on memory that wo...
bool isSpeculatable(Operation *op)
Returns true if the given operation is speculatable, i.e.
bool isOpTriviallyDead(Operation *op)
Return true if the given operation is unused, and has no side effects on memory that prevent erasing.
std::optional< llvm::SmallVector< MemoryEffects::EffectInstance > > getEffectsRecursively(Operation *rootOp)
Returns the side effects of an operation.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
bool hasEffect(Operation *op)
Returns "true" if op has an effect of type EffectTy.
bool hasUnknownEffects(Operation *op)
Return "true" if op has unknown effects.
static bool classof(const SideEffects::Effect *effect)
Include the definitions of the side effect interfaces.