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 
9 #include "mlir/Interfaces/SideEffectInterfaces.h"
10 
11 #include "llvm/ADT/SmallPtrSet.h"
12 
13 using namespace mlir;
14 
15 //===----------------------------------------------------------------------===//
16 // SideEffect Interfaces
17 //===----------------------------------------------------------------------===//
18 
19 /// Include the definitions of the side effect interfaces.
20 #include "mlir/Interfaces/SideEffectInterfaces.cpp.inc"
21 
22 //===----------------------------------------------------------------------===//
23 // MemoryEffects
24 //===----------------------------------------------------------------------===//
25 
classof(const SideEffects::Effect * effect)26 bool MemoryEffects::Effect::classof(const SideEffects::Effect *effect) {
27   return isa<Allocate, Free, Read, Write>(effect);
28 }
29 
30 //===----------------------------------------------------------------------===//
31 // SideEffect Utilities
32 //===----------------------------------------------------------------------===//
33 
isOpTriviallyDead(Operation * op)34 bool mlir::isOpTriviallyDead(Operation *op) {
35   return op->use_empty() && wouldOpBeTriviallyDead(op);
36 }
37 
38 /// Internal implementation of `mlir::wouldOpBeTriviallyDead` that also
39 /// considers terminator operations as dead if they have no side effects. This
40 /// allows for marking region operations as trivially dead without always being
41 /// conservative of terminators.
wouldOpBeTriviallyDeadImpl(Operation * rootOp)42 static bool wouldOpBeTriviallyDeadImpl(Operation *rootOp) {
43   // The set of operations to consider when checking for side effects.
44   SmallVector<Operation *, 1> effectingOps(1, rootOp);
45   while (!effectingOps.empty()) {
46     Operation *op = effectingOps.pop_back_val();
47 
48     // If the operation has recursive effects, push all of the nested operations
49     // on to the stack to consider.
50     bool hasRecursiveEffects = op->hasTrait<OpTrait::HasRecursiveSideEffects>();
51     if (hasRecursiveEffects) {
52       for (Region &region : op->getRegions()) {
53         for (auto &block : region) {
54           for (auto &nestedOp : block)
55             effectingOps.push_back(&nestedOp);
56         }
57       }
58     }
59 
60     // If the op has memory effects, try to characterize them to see if the op
61     // is trivially dead here.
62     if (auto effectInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
63       // Check to see if this op either has no effects, or only allocates/reads
64       // memory.
65       SmallVector<MemoryEffects::EffectInstance, 1> effects;
66       effectInterface.getEffects(effects);
67 
68       // Gather all results of this op that are allocated.
69       SmallPtrSet<Value, 4> allocResults;
70       for (const MemoryEffects::EffectInstance &it : effects)
71         if (isa<MemoryEffects::Allocate>(it.getEffect()) && it.getValue() &&
72             it.getValue().getDefiningOp() == op)
73           allocResults.insert(it.getValue());
74 
75       if (!llvm::all_of(effects, [&allocResults](
76                                      const MemoryEffects::EffectInstance &it) {
77             // We can drop effects if the value is an allocation and is a result
78             // of the operation.
79             if (allocResults.contains(it.getValue()))
80               return true;
81             // Otherwise, the effect must be a read.
82             return isa<MemoryEffects::Read>(it.getEffect());
83           })) {
84         return false;
85       }
86       continue;
87 
88       // Otherwise, if the op has recursive side effects we can treat the
89       // operation itself as having no effects.
90     }
91     if (hasRecursiveEffects)
92       continue;
93 
94     // If there were no effect interfaces, we treat this op as conservatively
95     // having effects.
96     return false;
97   }
98 
99   // If we get here, none of the operations had effects that prevented marking
100   // 'op' as dead.
101   return true;
102 }
103 
104 template <typename EffectTy>
hasSingleEffect(Operation * op,Value value)105 bool mlir::hasSingleEffect(Operation *op, Value value) {
106   auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
107   if (!memOp)
108     return false;
109   SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>, 4> effects;
110   memOp.getEffects(effects);
111   bool hasSingleEffectOnVal = false;
112   // Iterate through `effects` and check if an effect of type `EffectTy` and
113   // only of that type is present. A `value` to check the effect on may or may
114   // not have been provided.
115   for (auto &effect : effects) {
116     if (value && effect.getValue() != value)
117       continue;
118     hasSingleEffectOnVal = isa<EffectTy>(effect.getEffect());
119     if (!hasSingleEffectOnVal)
120       return false;
121   }
122   return hasSingleEffectOnVal;
123 }
124 
125 template bool mlir::hasSingleEffect<MemoryEffects::Allocate>(Operation *,
126                                                              Value);
127 template bool mlir::hasSingleEffect<MemoryEffects::Free>(Operation *, Value);
128 template bool mlir::hasSingleEffect<MemoryEffects::Read>(Operation *, Value);
129 template bool mlir::hasSingleEffect<MemoryEffects::Write>(Operation *, Value);
130 
wouldOpBeTriviallyDead(Operation * op)131 bool mlir::wouldOpBeTriviallyDead(Operation *op) {
132   if (op->mightHaveTrait<OpTrait::IsTerminator>())
133     return false;
134   return wouldOpBeTriviallyDeadImpl(op);
135 }
136