xref: /llvm-project/mlir/include/mlir/Interfaces/SideEffectInterfaces.h (revision 2c1ae801e1b66a09a15028ae4ba614e0911eec00)
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.
getEffectID()35     static TypeID getEffectID() { return TypeID::get<DerivedEffect>(); }
36 
37     /// 'classof' used to support llvm style cast functionality.
classof(const::mlir::SideEffects::Effect * effect)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.
get()43     static DerivedEffect *get() {
44       return BaseEffect::template get<DerivedEffect>();
45     }
46     using BaseEffect::get;
47 
48   protected:
Base()49     Base() : BaseEffect(BaseT::getEffectID()) {}
50   };
51 
52   /// Return the unique identifier for the base effects class.
getEffectID()53   TypeID getEffectID() const { return id; }
54 
55   /// Returns a unique instance for the given effect class.
56   template <typename DerivedEffect>
get()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:
Effect(TypeID id)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.
get()90     static DerivedResource *get() {
91       static DerivedResource instance;
92       return &instance;
93     }
94 
95     /// Return the unique identifier for the base resource class.
getResourceID()96     static TypeID getResourceID() { return TypeID::get<DerivedResource>(); }
97 
98     /// 'classof' used to support llvm style cast functionality.
classof(const Resource * resource)99     static bool classof(const Resource *resource) {
100       return resource->getResourceID() == BaseT::getResourceID();
101     }
102 
103   protected:
Base()104     Base() : BaseResource(BaseT::getResourceID()){};
105   };
106 
107   /// Return the unique identifier for the base resource class.
getResourceID()108   TypeID getResourceID() const { return id; }
109 
110   /// Return a string name of the resource.
111   virtual StringRef getName() = 0;
112 
113 protected:
Resource(TypeID id)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> {
getNameDefaultResource123   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> {
getNameAutomaticAllocationScopeResource130   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())
effect(effect)142       : effect(effect), resource(resource), stage(0),
143         effectOnFullRegion(false) {}
144   EffectInstance(EffectT *effect, int stage, bool effectOnFullRegion,
145                  Resource *resource = DefaultResource::get())
effect(effect)146       : effect(effect), resource(resource), stage(stage),
147         effectOnFullRegion(effectOnFullRegion) {}
148   template <typename T,
149             std::enable_if_t<
150                 llvm::is_one_of<T, OpOperand *, OpResult, BlockArgument>::value,
151                 bool> = true>
152   EffectInstance(EffectT *effect, T value,
153                  Resource *resource = DefaultResource::get())
effect(effect)154       : effect(effect), resource(resource), value(value), stage(0),
155         effectOnFullRegion(false) {}
156   template <typename T,
157             std::enable_if_t<
158                 llvm::is_one_of<T, OpOperand *, OpResult, BlockArgument>::value,
159                 bool> = true>
160   EffectInstance(EffectT *effect, T value, int stage, bool effectOnFullRegion,
161                  Resource *resource = DefaultResource::get())
effect(effect)162       : effect(effect), resource(resource), value(value), stage(stage),
163         effectOnFullRegion(effectOnFullRegion) {}
164   EffectInstance(EffectT *effect, SymbolRefAttr symbol,
165                  Resource *resource = DefaultResource::get())
effect(effect)166       : effect(effect), resource(resource), value(symbol), stage(0),
167         effectOnFullRegion(false) {}
168   EffectInstance(EffectT *effect, SymbolRefAttr symbol, int stage,
169                  bool effectOnFullRegion,
170                  Resource *resource = DefaultResource::get())
effect(effect)171       : effect(effect), resource(resource), value(symbol), stage(stage),
172         effectOnFullRegion(effectOnFullRegion) {}
173   EffectInstance(EffectT *effect, Attribute parameters,
174                  Resource *resource = DefaultResource::get())
effect(effect)175       : effect(effect), resource(resource), parameters(parameters), stage(0),
176         effectOnFullRegion(false) {}
177   EffectInstance(EffectT *effect, Attribute parameters, int stage,
178                  bool effectOnFullRegion,
179                  Resource *resource = DefaultResource::get())
effect(effect)180       : effect(effect), resource(resource), parameters(parameters),
181         stage(stage), effectOnFullRegion(effectOnFullRegion) {}
182   template <typename T,
183             std::enable_if_t<
184                 llvm::is_one_of<T, OpOperand *, OpResult, BlockArgument>::value,
185                 bool> = true>
186   EffectInstance(EffectT *effect, T value, Attribute parameters,
187                  Resource *resource = DefaultResource::get())
effect(effect)188       : effect(effect), resource(resource), value(value),
189         parameters(parameters), stage(0), effectOnFullRegion(false) {}
190   template <typename T,
191             std::enable_if_t<
192                 llvm::is_one_of<T, OpOperand *, OpResult, BlockArgument>::value,
193                 bool> = true>
194   EffectInstance(EffectT *effect, T value, Attribute parameters, int stage,
195                  bool effectOnFullRegion,
196                  Resource *resource = DefaultResource::get())
effect(effect)197       : effect(effect), resource(resource), value(value),
198         parameters(parameters), stage(stage),
199         effectOnFullRegion(effectOnFullRegion) {}
200   EffectInstance(EffectT *effect, SymbolRefAttr symbol, Attribute parameters,
201                  Resource *resource = DefaultResource::get())
effect(effect)202       : effect(effect), resource(resource), value(symbol),
203         parameters(parameters), stage(0), effectOnFullRegion(false) {}
204   EffectInstance(EffectT *effect, SymbolRefAttr symbol, Attribute parameters,
205                  int stage, bool effectOnFullRegion,
206                  Resource *resource = DefaultResource::get())
effect(effect)207       : effect(effect), resource(resource), value(symbol),
208         parameters(parameters), stage(stage),
209         effectOnFullRegion(effectOnFullRegion) {}
210 
211   /// Return the effect being applied.
getEffect()212   EffectT *getEffect() const { return effect; }
213 
214   /// Return the value the effect is applied on, or nullptr if there isn't a
215   /// known value being affected.
getValue()216   Value getValue() const {
217     if (!value || llvm::isa_and_present<SymbolRefAttr>(value)) {
218       return Value();
219     }
220     if (OpOperand *operand = llvm::dyn_cast_if_present<OpOperand *>(value)) {
221       return operand->get();
222     }
223     if (OpResult result = llvm::dyn_cast_if_present<OpResult>(value)) {
224       return result;
225     }
226     return cast_if_present<BlockArgument>(value);
227   }
228 
229   /// Returns the OpOperand effect is applied on, or nullptr if there isn't a
230   /// known value being effected.
231   template <typename T,
232             std::enable_if_t<
233                 llvm::is_one_of<T, OpOperand *, OpResult, BlockArgument>::value,
234                 bool> = true>
getEffectValue()235   T getEffectValue() const {
236     return value ? dyn_cast_if_present<T>(value) : nullptr;
237   }
238 
239   /// Return the symbol reference the effect is applied on, or nullptr if there
240   /// isn't a known smbol being affected.
getSymbolRef()241   SymbolRefAttr getSymbolRef() const {
242     return value ? llvm::dyn_cast_if_present<SymbolRefAttr>(value)
243                  : SymbolRefAttr();
244   }
245 
246   /// Return the resource that the effect applies to.
getResource()247   Resource *getResource() const { return resource; }
248 
249   /// Return the parameters of the effect, if any.
getParameters()250   Attribute getParameters() const { return parameters; }
251 
252   /// Return the effect happen stage.
getStage()253   int getStage() const { return stage; }
254 
255   /// Return if this side effect act on every single value of resource.
getEffectOnFullRegion()256   bool getEffectOnFullRegion() const { return effectOnFullRegion; }
257 
258 private:
259   /// The specific effect being applied.
260   EffectT *effect;
261 
262   /// The resource that the given value resides in.
263   Resource *resource;
264 
265   /// The Symbol, OpOperand, OpResult or BlockArgument that the effect applies
266   /// to. This is optionally null.
267   PointerUnion<SymbolRefAttr, OpOperand *, OpResult, BlockArgument> value;
268 
269   /// Additional parameters of the effect instance. An attribute is used for
270   /// type-safe structured storage and context-based uniquing. Concrete effects
271   /// can use this at their convenience. This is optionally null.
272   Attribute parameters;
273 
274   // The stage side effect happen. Side effect with a lower stage
275   // number happen earlier than those with a higher stage number
276   int stage;
277 
278   // Does this side effect act on every single value of resource.
279   bool effectOnFullRegion;
280 };
281 } // namespace SideEffects
282 
283 namespace Speculation {
284 /// This enum is returned from the `getSpeculatability` method in the
285 /// `ConditionallySpeculatable` op interface.
286 enum class Speculatability {
287   /// The Operation in question cannot be speculatively executed.  This could be
288   /// because it may invoke undefined behavior or have other side effects.
289   NotSpeculatable,
290 
291   // The Operation in question can be speculatively executed.  It does not have
292   // any side effects or undefined behavior.
293   Speculatable,
294 
295   // The Operation in question can be speculatively executed if all the
296   // operations in all attached regions can also be speculatively executed.
297   RecursivelySpeculatable,
298 };
299 
300 constexpr auto NotSpeculatable = Speculatability::NotSpeculatable;
301 constexpr auto Speculatable = Speculatability::Speculatable;
302 constexpr auto RecursivelySpeculatable =
303     Speculatability::RecursivelySpeculatable;
304 } // namespace Speculation
305 
306 //===----------------------------------------------------------------------===//
307 // SideEffect Traits
308 //===----------------------------------------------------------------------===//
309 
310 namespace OpTrait {
311 /// This trait indicates that the memory effects of an operation includes the
312 /// effects of operations nested within its regions. If the operation has no
313 /// derived effects interfaces, the operation itself can be assumed to have no
314 /// memory effects.
315 template <typename ConcreteType>
316 class HasRecursiveMemoryEffects
317     : public TraitBase<ConcreteType, HasRecursiveMemoryEffects> {};
318 
319 /// This trait marks an op (which must be tagged as implementing the
320 /// ConditionallySpeculatable interface) as being recursively speculatable.
321 /// This means that said op can be speculated only if all the instructions in
322 /// all the regions attached to the op can be speculated.
323 template <typename ConcreteType>
324 struct RecursivelySpeculatableImplTrait
325     : public TraitBase<ConcreteType, RecursivelySpeculatableImplTrait> {
326 
getSpeculatabilityRecursivelySpeculatableImplTrait327   Speculation::Speculatability getSpeculatability() {
328     return Speculation::RecursivelySpeculatable;
329   }
330 };
331 
332 /// This trait marks an op (which must be tagged as implementing the
333 /// ConditionallySpeculatable interface) as being always speculatable.
334 template <typename ConcreteType>
335 struct AlwaysSpeculatableImplTrait
336     : public TraitBase<ConcreteType, AlwaysSpeculatableImplTrait> {
337 
getSpeculatabilityAlwaysSpeculatableImplTrait338   Speculation::Speculatability getSpeculatability() {
339     return Speculation::Speculatable;
340   }
341 };
342 } // namespace OpTrait
343 
344 //===----------------------------------------------------------------------===//
345 // Operation Memory-Effect Modeling
346 //===----------------------------------------------------------------------===//
347 
348 namespace MemoryEffects {
349 /// This class represents the base class used for memory effects.
350 struct Effect : public SideEffects::Effect {
351   using SideEffects::Effect::Effect;
352 
353   /// A base class for memory effects that provides helper utilities.
354   template <typename DerivedEffect>
355   using Base = SideEffects::Effect::Base<DerivedEffect, Effect>;
356 
357   static bool classof(const SideEffects::Effect *effect);
358 };
359 using EffectInstance = SideEffects::EffectInstance<Effect>;
360 
361 /// The following effect indicates that the operation allocates from some
362 /// resource. An 'allocate' effect implies only allocation of the resource, and
363 /// not any visible mutation or dereference.
364 struct Allocate : public Effect::Base<Allocate> {};
365 
366 /// The following effect indicates that the operation frees some resource that
367 /// has been allocated. An 'allocate' effect implies only de-allocation of the
368 /// resource, and not any visible allocation, mutation or dereference.
369 struct Free : public Effect::Base<Free> {};
370 
371 /// The following effect indicates that the operation reads from some resource.
372 /// A 'read' effect implies only dereferencing of the resource, and not any
373 /// visible mutation.
374 struct Read : public Effect::Base<Read> {};
375 
376 /// The following effect indicates that the operation writes to some resource. A
377 /// 'write' effect implies only mutating a resource, and not any visible
378 /// dereference or read.
379 struct Write : public Effect::Base<Write> {};
380 } // namespace MemoryEffects
381 
382 //===----------------------------------------------------------------------===//
383 // SideEffect Utilities
384 //===----------------------------------------------------------------------===//
385 
386 /// Returns true if `op` has only an effect of type `EffectTy`.
387 template <typename EffectTy>
388 bool hasSingleEffect(Operation *op);
389 
390 /// Returns true if `op` has only an effect of type `EffectTy` (and of no other
391 /// type) on `value`.
392 template <typename EffectTy>
393 bool hasSingleEffect(Operation *op, Value value);
394 
395 /// Returns true if `op` has only an effect of type `EffectTy` (and of no other
396 /// type) on `value` of type `ValueTy`.
397 template <typename ValueTy, typename EffectTy>
398 bool hasSingleEffect(Operation *op, ValueTy value);
399 
400 /// Returns true if `op` has an effect of type `EffectTy`.
401 template <typename... EffectTys>
402 bool hasEffect(Operation *op);
403 
404 /// Returns true if `op` has an effect of type `EffectTy` on `value`.
405 template <typename... EffectTys>
406 bool hasEffect(Operation *op, Value value);
407 
408 /// Returns true if `op` has an effect of type `EffectTy` on `value` of type
409 /// `ValueTy`.
410 template <typename ValueTy, typename... EffectTys>
411 bool hasEffect(Operation *op, ValueTy value);
412 
413 /// Return true if the given operation is unused, and has no side effects on
414 /// memory that prevent erasing.
415 bool isOpTriviallyDead(Operation *op);
416 
417 /// Return true if the given operation would be dead if unused, and has no side
418 /// effects on memory that would prevent erasing. This is equivalent to checking
419 /// `isOpTriviallyDead` if `op` was unused.
420 ///
421 /// Note: Terminators and symbols are never considered to be trivially dead.
422 bool wouldOpBeTriviallyDead(Operation *op);
423 
424 /// Returns true if the given operation is free of memory effects.
425 ///
426 /// An operation is free of memory effects if its implementation of
427 /// `MemoryEffectOpInterface` indicates that it has no memory effects. For
428 /// example, it may implement `NoMemoryEffect` in ODS. Alternatively, if the
429 /// operation has the `HasRecursiveMemoryEffects` trait, then it is free of
430 /// memory effects if all of its nested operations are free of memory effects.
431 ///
432 /// If the operation has both, then it is free of memory effects if both
433 /// conditions are satisfied.
434 bool isMemoryEffectFree(Operation *op);
435 
436 /// Returns the side effects of an operation. If the operation has
437 /// RecursiveMemoryEffects, include all side effects of child operations.
438 ///
439 /// std::nullopt indicates that an option did not have a memory effect interface
440 /// and so no result could be obtained. An empty vector indicates that there
441 /// were no memory effects found (but every operation implemented the memory
442 /// effect interface or has RecursiveMemoryEffects). If the vector contains
443 /// multiple effects, these effects may be duplicates.
444 std::optional<llvm::SmallVector<MemoryEffects::EffectInstance>>
445 getEffectsRecursively(Operation *rootOp);
446 
447 /// Returns true if the given operation is speculatable, i.e. has no undefined
448 /// behavior or other side effects.
449 ///
450 /// An operation can indicate that it is speculatable by implementing the
451 /// getSpeculatability hook in the ConditionallySpeculatable op interface.
452 bool isSpeculatable(Operation *op);
453 
454 /// Returns true if the given operation is pure, i.e., is speculatable that does
455 /// not touch memory.
456 ///
457 /// This function is the C++ equivalent of the `Pure` trait.
458 bool isPure(Operation *op);
459 
460 } // namespace mlir
461 
462 //===----------------------------------------------------------------------===//
463 // SideEffect Interfaces
464 //===----------------------------------------------------------------------===//
465 
466 /// Include the definitions of the side effect interfaces.
467 #include "mlir/Interfaces/SideEffectInterfaces.h.inc"
468 
469 #endif // MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
470