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