1 | //===- SideEffectInterfaces.h - SideEffect in MLIR --------------*- C++ -*-===// |
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 | // This file contains traits, interfaces, and utilities for defining and |
10 | // querying the side effects of an operation. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef MLIR_INTERFACES_SIDEEFFECTINTERFACES_H |
15 | #define MLIR_INTERFACES_SIDEEFFECTINTERFACES_H |
16 | |
17 | #include "mlir/IR/OpDefinition.h" |
18 | |
19 | namespace mlir { |
20 | namespace SideEffects { |
21 | //===----------------------------------------------------------------------===// |
22 | // Effects |
23 | //===----------------------------------------------------------------------===// |
24 | |
25 | /// This class represents a base class for a specific effect type. |
26 | class Effect { |
27 | public: |
28 | /// This base class is used for derived effects that are non-parametric. |
29 | template <typename DerivedEffect, typename BaseEffect = Effect> |
30 | class Base : public BaseEffect { |
31 | public: |
32 | using BaseT = Base<DerivedEffect>; |
33 | |
34 | /// Return the unique identifier for the base effects class. |
35 | static TypeID getEffectID() { return TypeID::get<DerivedEffect>(); } |
36 | |
37 | /// 'classof' used to support llvm style cast functionality. |
38 | static bool classof(const ::mlir::SideEffects::Effect *effect) { |
39 | return effect->getEffectID() == BaseT::getEffectID(); |
40 | } |
41 | |
42 | /// Returns a unique instance for the derived effect class. |
43 | static DerivedEffect *get() { |
44 | return BaseEffect::template get<DerivedEffect>(); |
45 | } |
46 | using BaseEffect::get; |
47 | |
48 | protected: |
49 | Base() : BaseEffect(BaseT::getEffectID()) {} |
50 | }; |
51 | |
52 | /// Return the unique identifier for the base effects class. |
53 | TypeID getEffectID() const { return id; } |
54 | |
55 | /// Returns a unique instance for the given effect class. |
56 | template <typename DerivedEffect> |
57 | static DerivedEffect *get() { |
58 | static_assert(std::is_base_of<Effect, DerivedEffect>::value, |
59 | "expected DerivedEffect to inherit from Effect" ); |
60 | |
61 | static DerivedEffect instance; |
62 | return &instance; |
63 | } |
64 | |
65 | protected: |
66 | Effect(TypeID id) : id(id) {} |
67 | |
68 | private: |
69 | /// The id of the derived effect class. |
70 | TypeID id; |
71 | }; |
72 | |
73 | //===----------------------------------------------------------------------===// |
74 | // Resources |
75 | //===----------------------------------------------------------------------===// |
76 | |
77 | /// This class represents a specific resource that an effect applies to. This |
78 | /// class represents an abstract interface for a given resource. |
79 | class Resource { |
80 | public: |
81 | virtual ~Resource() = default; |
82 | |
83 | /// This base class is used for derived effects that are non-parametric. |
84 | template <typename DerivedResource, typename BaseResource = Resource> |
85 | class Base : public BaseResource { |
86 | public: |
87 | using BaseT = Base<DerivedResource>; |
88 | |
89 | /// Returns a unique instance for the given effect class. |
90 | static DerivedResource *get() { |
91 | static DerivedResource instance; |
92 | return &instance; |
93 | } |
94 | |
95 | /// Return the unique identifier for the base resource class. |
96 | static TypeID getResourceID() { return TypeID::get<DerivedResource>(); } |
97 | |
98 | /// 'classof' used to support llvm style cast functionality. |
99 | static bool classof(const Resource *resource) { |
100 | return resource->getResourceID() == BaseT::getResourceID(); |
101 | } |
102 | |
103 | protected: |
104 | Base() : BaseResource(BaseT::getResourceID()){}; |
105 | }; |
106 | |
107 | /// Return the unique identifier for the base resource class. |
108 | TypeID getResourceID() const { return id; } |
109 | |
110 | /// Return a string name of the resource. |
111 | virtual StringRef getName() = 0; |
112 | |
113 | protected: |
114 | Resource(TypeID id) : id(id) {} |
115 | |
116 | private: |
117 | /// The id of the derived resource class. |
118 | TypeID id; |
119 | }; |
120 | |
121 | /// A conservative default resource kind. |
122 | struct DefaultResource : public Resource::Base<DefaultResource> { |
123 | StringRef getName() final { return "<Default>" ; } |
124 | }; |
125 | |
126 | /// An automatic allocation-scope resource that is valid in the context of a |
127 | /// parent AutomaticAllocationScope trait. |
128 | struct AutomaticAllocationScopeResource |
129 | : public Resource::Base<AutomaticAllocationScopeResource> { |
130 | StringRef getName() final { return "AutomaticAllocationScope" ; } |
131 | }; |
132 | |
133 | /// This class represents a specific instance of an effect. It contains the |
134 | /// effect being applied, a resource that corresponds to where the effect is |
135 | /// applied, and an optional symbol reference or value(either operand, result, |
136 | /// or region entry argument) that the effect is applied to, and an optional |
137 | /// parameters attribute further specifying the details of the effect. |
138 | template <typename EffectT> |
139 | class EffectInstance { |
140 | public: |
141 | EffectInstance(EffectT *effect, Resource *resource = DefaultResource::get()) |
142 | : effect(effect), resource(resource), stage(0), |
143 | effectOnFullRegion(false) {} |
144 | EffectInstance(EffectT *effect, int stage, bool effectOnFullRegion, |
145 | Resource *resource = DefaultResource::get()) |
146 | : effect(effect), resource(resource), stage(stage), |
147 | effectOnFullRegion(effectOnFullRegion) {} |
148 | EffectInstance(EffectT *effect, Value value, |
149 | Resource *resource = DefaultResource::get()) |
150 | : effect(effect), resource(resource), value(value), stage(0), |
151 | effectOnFullRegion(false) {} |
152 | EffectInstance(EffectT *effect, Value value, int stage, |
153 | bool effectOnFullRegion, |
154 | Resource *resource = DefaultResource::get()) |
155 | : effect(effect), resource(resource), value(value), stage(stage), |
156 | effectOnFullRegion(effectOnFullRegion) {} |
157 | EffectInstance(EffectT *effect, SymbolRefAttr symbol, |
158 | Resource *resource = DefaultResource::get()) |
159 | : effect(effect), resource(resource), value(symbol), stage(0), |
160 | effectOnFullRegion(false) {} |
161 | EffectInstance(EffectT *effect, SymbolRefAttr symbol, int stage, |
162 | bool effectOnFullRegion, |
163 | Resource *resource = DefaultResource::get()) |
164 | : effect(effect), resource(resource), value(symbol), stage(stage), |
165 | effectOnFullRegion(effectOnFullRegion) {} |
166 | EffectInstance(EffectT *effect, Attribute parameters, |
167 | Resource *resource = DefaultResource::get()) |
168 | : effect(effect), resource(resource), parameters(parameters), stage(0), |
169 | effectOnFullRegion(false) {} |
170 | EffectInstance(EffectT *effect, Attribute parameters, int stage, |
171 | bool effectOnFullRegion, |
172 | Resource *resource = DefaultResource::get()) |
173 | : effect(effect), resource(resource), parameters(parameters), |
174 | stage(stage), effectOnFullRegion(effectOnFullRegion) {} |
175 | EffectInstance(EffectT *effect, Value value, Attribute parameters, |
176 | Resource *resource = DefaultResource::get()) |
177 | : effect(effect), resource(resource), value(value), |
178 | parameters(parameters), stage(0), effectOnFullRegion(false) {} |
179 | EffectInstance(EffectT *effect, Value value, Attribute parameters, int stage, |
180 | bool effectOnFullRegion, |
181 | Resource *resource = DefaultResource::get()) |
182 | : effect(effect), resource(resource), value(value), |
183 | parameters(parameters), stage(stage), |
184 | effectOnFullRegion(effectOnFullRegion) {} |
185 | EffectInstance(EffectT *effect, SymbolRefAttr symbol, Attribute parameters, |
186 | Resource *resource = DefaultResource::get()) |
187 | : effect(effect), resource(resource), value(symbol), |
188 | parameters(parameters), stage(0), effectOnFullRegion(false) {} |
189 | EffectInstance(EffectT *effect, SymbolRefAttr symbol, Attribute parameters, |
190 | int stage, bool effectOnFullRegion, |
191 | Resource *resource = DefaultResource::get()) |
192 | : effect(effect), resource(resource), value(symbol), |
193 | parameters(parameters), stage(stage), |
194 | effectOnFullRegion(effectOnFullRegion) {} |
195 | |
196 | /// Return the effect being applied. |
197 | EffectT *getEffect() const { return effect; } |
198 | |
199 | /// Return the value the effect is applied on, or nullptr if there isn't a |
200 | /// known value being affected. |
201 | Value getValue() const { |
202 | return value ? llvm::dyn_cast_if_present<Value>(Val: value) : Value(); |
203 | } |
204 | |
205 | /// Return the symbol reference the effect is applied on, or nullptr if there |
206 | /// isn't a known smbol being affected. |
207 | SymbolRefAttr getSymbolRef() const { |
208 | return value ? llvm::dyn_cast_if_present<SymbolRefAttr>(value) |
209 | : SymbolRefAttr(); |
210 | } |
211 | |
212 | /// Return the resource that the effect applies to. |
213 | Resource *getResource() const { return resource; } |
214 | |
215 | /// Return the parameters of the effect, if any. |
216 | Attribute getParameters() const { return parameters; } |
217 | |
218 | /// Return the effect happen stage. |
219 | int getStage() const { return stage; } |
220 | |
221 | /// Return if this side effect act on every single value of resource. |
222 | bool getEffectOnFullRegion() const { return effectOnFullRegion; } |
223 | |
224 | private: |
225 | /// The specific effect being applied. |
226 | EffectT *effect; |
227 | |
228 | /// The resource that the given value resides in. |
229 | Resource *resource; |
230 | |
231 | /// The Symbol or Value that the effect applies to. This is optionally null. |
232 | PointerUnion<SymbolRefAttr, Value> value; |
233 | |
234 | /// Additional parameters of the effect instance. An attribute is used for |
235 | /// type-safe structured storage and context-based uniquing. Concrete effects |
236 | /// can use this at their convenience. This is optionally null. |
237 | Attribute parameters; |
238 | |
239 | // The stage side effect happen. Side effect with a lower stage |
240 | // number happen earlier than those with a higher stage number |
241 | int stage; |
242 | |
243 | // Does this side effect act on every single value of resource. |
244 | bool effectOnFullRegion; |
245 | }; |
246 | } // namespace SideEffects |
247 | |
248 | namespace Speculation { |
249 | /// This enum is returned from the `getSpeculatability` method in the |
250 | /// `ConditionallySpeculatable` op interface. |
251 | enum class Speculatability { |
252 | /// The Operation in question cannot be speculatively executed. This could be |
253 | /// because it may invoke undefined behavior or have other side effects. |
254 | NotSpeculatable, |
255 | |
256 | // The Operation in question can be speculatively executed. It does not have |
257 | // any side effects or undefined behavior. |
258 | Speculatable, |
259 | |
260 | // The Operation in question can be speculatively executed if all the |
261 | // operations in all attached regions can also be speculatively executed. |
262 | RecursivelySpeculatable, |
263 | }; |
264 | |
265 | constexpr auto NotSpeculatable = Speculatability::NotSpeculatable; |
266 | constexpr auto Speculatable = Speculatability::Speculatable; |
267 | constexpr auto RecursivelySpeculatable = |
268 | Speculatability::RecursivelySpeculatable; |
269 | } // namespace Speculation |
270 | |
271 | //===----------------------------------------------------------------------===// |
272 | // SideEffect Traits |
273 | //===----------------------------------------------------------------------===// |
274 | |
275 | namespace OpTrait { |
276 | /// This trait indicates that the memory effects of an operation includes the |
277 | /// effects of operations nested within its regions. If the operation has no |
278 | /// derived effects interfaces, the operation itself can be assumed to have no |
279 | /// memory effects. |
280 | template <typename ConcreteType> |
281 | class HasRecursiveMemoryEffects |
282 | : public TraitBase<ConcreteType, HasRecursiveMemoryEffects> {}; |
283 | |
284 | /// This trait marks an op (which must be tagged as implementing the |
285 | /// ConditionallySpeculatable interface) as being recursively speculatable. |
286 | /// This means that said op can be speculated only if all the instructions in |
287 | /// all the regions attached to the op can be speculated. |
288 | template <typename ConcreteType> |
289 | struct RecursivelySpeculatableImplTrait |
290 | : public TraitBase<ConcreteType, RecursivelySpeculatableImplTrait> { |
291 | |
292 | Speculation::Speculatability getSpeculatability() { |
293 | return Speculation::RecursivelySpeculatable; |
294 | } |
295 | }; |
296 | |
297 | /// This trait marks an op (which must be tagged as implementing the |
298 | /// ConditionallySpeculatable interface) as being always speculatable. |
299 | template <typename ConcreteType> |
300 | struct AlwaysSpeculatableImplTrait |
301 | : public TraitBase<ConcreteType, AlwaysSpeculatableImplTrait> { |
302 | |
303 | Speculation::Speculatability getSpeculatability() { |
304 | return Speculation::Speculatable; |
305 | } |
306 | }; |
307 | } // namespace OpTrait |
308 | |
309 | //===----------------------------------------------------------------------===// |
310 | // Operation Memory-Effect Modeling |
311 | //===----------------------------------------------------------------------===// |
312 | |
313 | namespace MemoryEffects { |
314 | /// This class represents the base class used for memory effects. |
315 | struct Effect : public SideEffects::Effect { |
316 | using SideEffects::Effect::Effect; |
317 | |
318 | /// A base class for memory effects that provides helper utilities. |
319 | template <typename DerivedEffect> |
320 | using Base = SideEffects::Effect::Base<DerivedEffect, Effect>; |
321 | |
322 | static bool classof(const SideEffects::Effect *effect); |
323 | }; |
324 | using EffectInstance = SideEffects::EffectInstance<Effect>; |
325 | |
326 | /// The following effect indicates that the operation allocates from some |
327 | /// resource. An 'allocate' effect implies only allocation of the resource, and |
328 | /// not any visible mutation or dereference. |
329 | struct Allocate : public Effect::Base<Allocate> {}; |
330 | |
331 | /// The following effect indicates that the operation frees some resource that |
332 | /// has been allocated. An 'allocate' effect implies only de-allocation of the |
333 | /// resource, and not any visible allocation, mutation or dereference. |
334 | struct Free : public Effect::Base<Free> {}; |
335 | |
336 | /// The following effect indicates that the operation reads from some resource. |
337 | /// A 'read' effect implies only dereferencing of the resource, and not any |
338 | /// visible mutation. |
339 | struct Read : public Effect::Base<Read> {}; |
340 | |
341 | /// The following effect indicates that the operation writes to some resource. A |
342 | /// 'write' effect implies only mutating a resource, and not any visible |
343 | /// dereference or read. |
344 | struct Write : public Effect::Base<Write> {}; |
345 | } // namespace MemoryEffects |
346 | |
347 | //===----------------------------------------------------------------------===// |
348 | // SideEffect Utilities |
349 | //===----------------------------------------------------------------------===// |
350 | |
351 | /// Returns true if `op` has only an effect of type `EffectTy` (and of no other |
352 | /// type) on `value`. If no value is provided, simply check if effects of that |
353 | /// type and only of that type are present. |
354 | template <typename EffectTy> |
355 | bool hasSingleEffect(Operation *op, Value value = nullptr); |
356 | |
357 | /// Returns true if `op` has an effect of type `EffectTy` on `value`. If no |
358 | /// `value` is provided, simply check if effects of the given type(s) are |
359 | /// present. |
360 | template <typename... EffectTys> |
361 | bool hasEffect(Operation *op, Value value = nullptr); |
362 | |
363 | /// Return true if the given operation is unused, and has no side effects on |
364 | /// memory that prevent erasing. |
365 | bool isOpTriviallyDead(Operation *op); |
366 | |
367 | /// Return true if the given operation would be dead if unused, and has no side |
368 | /// effects on memory that would prevent erasing. This is equivalent to checking |
369 | /// `isOpTriviallyDead` if `op` was unused. |
370 | /// |
371 | /// Note: Terminators and symbols are never considered to be trivially dead. |
372 | bool wouldOpBeTriviallyDead(Operation *op); |
373 | |
374 | /// Returns true if the given operation is free of memory effects. |
375 | /// |
376 | /// An operation is free of memory effects if its implementation of |
377 | /// `MemoryEffectOpInterface` indicates that it has no memory effects. For |
378 | /// example, it may implement `NoMemoryEffect` in ODS. Alternatively, if the |
379 | /// operation has the `HasRecursiveMemoryEffects` trait, then it is free of |
380 | /// memory effects if all of its nested operations are free of memory effects. |
381 | /// |
382 | /// If the operation has both, then it is free of memory effects if both |
383 | /// conditions are satisfied. |
384 | bool isMemoryEffectFree(Operation *op); |
385 | |
386 | /// Returns the side effects of an operation. If the operation has |
387 | /// RecursiveMemoryEffects, include all side effects of child operations. |
388 | /// |
389 | /// std::nullopt indicates that an option did not have a memory effect interface |
390 | /// and so no result could be obtained. An empty vector indicates that there |
391 | /// were no memory effects found (but every operation implemented the memory |
392 | /// effect interface or has RecursiveMemoryEffects). If the vector contains |
393 | /// multiple effects, these effects may be duplicates. |
394 | std::optional<llvm::SmallVector<MemoryEffects::EffectInstance>> |
395 | getEffectsRecursively(Operation *rootOp); |
396 | |
397 | /// Returns true if the given operation is speculatable, i.e. has no undefined |
398 | /// behavior or other side effects. |
399 | /// |
400 | /// An operation can indicate that it is speculatable by implementing the |
401 | /// getSpeculatability hook in the ConditionallySpeculatable op interface. |
402 | bool isSpeculatable(Operation *op); |
403 | |
404 | /// Returns true if the given operation is pure, i.e., is speculatable that does |
405 | /// not touch memory. |
406 | /// |
407 | /// This function is the C++ equivalent of the `Pure` trait. |
408 | bool isPure(Operation *op); |
409 | |
410 | } // namespace mlir |
411 | |
412 | //===----------------------------------------------------------------------===// |
413 | // SideEffect Interfaces |
414 | //===----------------------------------------------------------------------===// |
415 | |
416 | /// Include the definitions of the side effect interfaces. |
417 | #include "mlir/Interfaces/SideEffectInterfaces.h.inc" |
418 | |
419 | #endif // MLIR_INTERFACES_SIDEEFFECTINTERFACES_H |
420 | |