[Mlir-commits] [mlir] f892358 - [mlir][SideEffects] Define a set of interfaces and traits for defining side effects

River Riddle llvmlistbot at llvm.org
Fri Mar 6 14:05:07 PST 2020


Author: River Riddle
Date: 2020-03-06T14:04:36-08:00
New Revision: f8923584da83be461aafab3f45cc41f872fa7d74

URL: https://github.com/llvm/llvm-project/commit/f8923584da83be461aafab3f45cc41f872fa7d74
DIFF: https://github.com/llvm/llvm-project/commit/f8923584da83be461aafab3f45cc41f872fa7d74.diff

LOG: [mlir][SideEffects] Define a set of interfaces and traits for defining side effects

This revision introduces the infrastructure for defining side-effects and attaching them to operations. This infrastructure allows for defining different types of side effects, that don't interact with each other, but use the same internal mechanisms. At the base of this is an interface that allows operations to specify the different effect instances that are exhibited by a specific operation instance. An effect instance is comprised of the following:

* Effect: The specific effect being applied.
  For memory related effects this may be reading from memory, storing to memory, etc.

* Value: A specific value, either operand/result/region argument, the effect pertains to.

* Resource: This is a global entity that represents the domain within which the effect is being applied.

MLIR serves many different abstractions, which cover many different domains. Simple effects are may have very different context, for example writing to an in-memory buffer vs a database. This revision defines uses this infrastructure to define a set of initial MemoryEffects. The are effects that generally correspond to memory of some kind; Allocate, Free, Read, Write.

This set of memory effects will be used in follow revisions to generalize various parts of the compiler, and make others more powerful(e.g. DCE).

This infrastructure was originally proposed here:
https://groups.google.com/a/tensorflow.org/g/mlir/c/v2mNl4vFCUM

Differential Revision: https://reviews.llvm.org/D74439

Added: 
    mlir/include/mlir/IR/SideEffects.td
    mlir/test/IR/test-side-effects.mlir
    mlir/test/lib/IR/TestSideEffects.cpp

Modified: 
    mlir/include/mlir/IR/CMakeLists.txt
    mlir/include/mlir/IR/OpBase.td
    mlir/include/mlir/IR/OpDefinition.h
    mlir/lib/IR/CMakeLists.txt
    mlir/lib/IR/Operation.cpp
    mlir/lib/Translation/CMakeLists.txt
    mlir/test/lib/IR/CMakeLists.txt
    mlir/test/lib/TestDialect/TestDialect.cpp
    mlir/test/lib/TestDialect/TestOps.td
    mlir/tools/mlir-opt/mlir-opt.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/IR/CMakeLists.txt b/mlir/include/mlir/IR/CMakeLists.txt
index 555b16fd29d0..b4252c743cfb 100644
--- a/mlir/include/mlir/IR/CMakeLists.txt
+++ b/mlir/include/mlir/IR/CMakeLists.txt
@@ -2,3 +2,8 @@ set(LLVM_TARGET_DEFINITIONS OpAsmInterface.td)
 mlir_tablegen(OpAsmInterface.h.inc -gen-op-interface-decls)
 mlir_tablegen(OpAsmInterface.cpp.inc -gen-op-interface-defs)
 add_public_tablegen_target(MLIROpAsmInterfacesIncGen)
+
+set(LLVM_TARGET_DEFINITIONS SideEffects.td)
+mlir_tablegen(SideEffectInterfaces.h.inc -gen-op-interface-decls)
+mlir_tablegen(SideEffectInterfaces.cpp.inc -gen-op-interface-defs)
+add_public_tablegen_target(MLIRSideEffectOpInterfacesIncGen)

diff  --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td
index 2bc654628aba..1c51fc86b444 100644
--- a/mlir/include/mlir/IR/OpBase.td
+++ b/mlir/include/mlir/IR/OpBase.td
@@ -1534,6 +1534,8 @@ def IsolatedFromAbove : NativeOpTrait<"IsIsolatedFromAbove">;
 def ResultsAreFloatLike : NativeOpTrait<"ResultsAreFloatLike">;
 // Op has no side effect.
 def NoSideEffect : NativeOpTrait<"HasNoSideEffect">;
+// Op has recursively computed side effects.
+def RecursiveSideEffects : NativeOpTrait<"HasRecursiveSideEffects">;
 // Op has the same operand type.
 def SameTypeOperands : NativeOpTrait<"SameTypeOperands">;
 // Op has same shape for all operands.

diff  --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h
index efbe31bad599..fe94f256035a 100644
--- a/mlir/include/mlir/IR/OpDefinition.h
+++ b/mlir/include/mlir/IR/OpDefinition.h
@@ -870,16 +870,6 @@ class IsCommutative : public TraitBase<ConcreteType, IsCommutative> {
   }
 };
 
-/// This class adds property that the operation has no side effects.
-template <typename ConcreteType>
-class HasNoSideEffect : public TraitBase<ConcreteType, HasNoSideEffect> {
-public:
-  static AbstractOperation::OperationProperties getTraitProperties() {
-    return static_cast<AbstractOperation::OperationProperties>(
-        OperationProperty::NoSideEffect);
-  }
-};
-
 /// This class verifies that all operands of the specified op have a float type,
 /// a vector thereof, or a tensor thereof.
 template <typename ConcreteType>
@@ -1287,6 +1277,222 @@ class OpInterface : public Op<ConcreteType> {
   Concept *impl;
 };
 
+//===----------------------------------------------------------------------===//
+// Operation Side-Effect Modeling
+//===----------------------------------------------------------------------===//
+
+// TODO(riverriddle) This should be in its own file in a proper
+// traits/interfaces directory. Move it there when we have one.
+
+namespace SideEffects {
+//===--------------------------------------------------------------------===//
+// Effects
+
+/// This class represents a base class for a specific effect type.
+class Effect {
+public:
+  /// This base class is used for derived effects that are non-parametric.
+  template <typename DerivedEffect, typename BaseEffect = Effect>
+  class Base : public BaseEffect {
+  public:
+    using BaseT = Base<DerivedEffect>;
+
+    /// Return the unique identifier for the base effects class.
+    static ClassID *getEffectID() { return ClassID::getID<DerivedEffect>(); }
+
+    /// 'classof' used to support llvm style cast functionality.
+    static bool classof(const Effect *effect) {
+      return effect->getEffectID() == BaseT::getEffectID();
+    }
+
+    /// Returns a unique instance for the derived effect class.
+    static DerivedEffect *get() {
+      return BaseEffect::template get<DerivedEffect>();
+    }
+    using BaseEffect::get;
+
+  protected:
+    Base() : BaseEffect(BaseT::getEffectID()){};
+  };
+
+  /// Return the unique identifier for the base effects class.
+  ClassID *getEffectID() const { return id; }
+
+  /// Returns a unique instance for the given effect class.
+  template <typename DerivedEffect> static DerivedEffect *get() {
+    static_assert(std::is_base_of<Effect, DerivedEffect>::value,
+                  "expected DerivedEffect to inherit from Effect");
+
+    static DerivedEffect instance;
+    return &instance;
+  }
+
+protected:
+  Effect(ClassID *id) : id(id) {}
+
+private:
+  /// The id of the derived effect class.
+  ClassID *id;
+};
+
+//===--------------------------------------------------------------------===//
+// Resources
+
+/// This class represents a specific resource that an effect applies to. This
+/// class represents an abstract interface for a given resource.
+class Resource {
+public:
+  virtual ~Resource() {}
+
+  /// This base class is used for derived effects that are non-parametric.
+  template <typename DerivedResource, typename BaseResource = Resource>
+  class Base : public BaseResource {
+  public:
+    using BaseT = Base<DerivedResource>;
+
+    /// Returns a unique instance for the given effect class.
+    static DerivedResource *get() {
+      static DerivedResource instance;
+      return &instance;
+    }
+
+    /// Return the unique identifier for the base resource class.
+    static ClassID *getResourceID() {
+      return ClassID::getID<DerivedResource>();
+    }
+
+    /// 'classof' used to support llvm style cast functionality.
+    static bool classof(const Resource *resource) {
+      return resource->getResourceID() == BaseT::getResourceID();
+    }
+
+  protected:
+    Base() : BaseResource(BaseT::getResourceID()){};
+  };
+
+  /// Return the unique identifier for the base resource class.
+  ClassID *getResourceID() const { return id; }
+
+  /// Return a string name of the resource.
+  virtual StringRef getName() = 0;
+
+protected:
+  Resource(ClassID *id) : id(id) {}
+
+private:
+  /// The id of the derived resource class.
+  ClassID *id;
+};
+
+/// A conservative default resource kind.
+struct DefaultResource : public Resource::Base<DefaultResource> {
+  StringRef getName() final { return "<Default>"; }
+};
+
+/// This class represents a specific instance of an effect. It contains the
+/// effect being applied, a resource that corresponds to where the effect is
+/// applied, and an optional value(either operand, result, or region entry
+/// argument) that the effect is applied to.
+template <typename EffectT> class EffectInstance {
+public:
+  EffectInstance(EffectT *effect, Resource *resource = DefaultResource::get())
+      : effect(effect), resource(resource) {}
+  EffectInstance(EffectT *effect, Value value,
+                 Resource *resource = DefaultResource::get())
+      : effect(effect), resource(resource), value(value) {}
+
+  /// Return the effect being applied.
+  EffectT *getEffect() const { return effect; }
+
+  /// Return the value the effect is applied on, or nullptr if there isn't a
+  /// known value being affected.
+  Value getValue() const { return value; }
+
+  /// Return the resource that the effect applies to.
+  Resource *getResource() const { return resource; }
+
+private:
+  /// The specific effect being applied.
+  EffectT *effect;
+
+  /// The resource that the given value resides in.
+  Resource *resource;
+
+  /// The value that the effect applies to. This is optionally null.
+  Value value;
+};
+} // namespace SideEffects
+
+//===----------------------------------------------------------------------===//
+// SideEffect Traits
+
+namespace OpTrait {
+/// This trait indicates that an operation never has side effects.
+template <typename ConcreteType>
+class HasNoSideEffect : public TraitBase<ConcreteType, HasNoSideEffect> {
+public:
+  static AbstractOperation::OperationProperties getTraitProperties() {
+    return static_cast<AbstractOperation::OperationProperties>(
+        OperationProperty::NoSideEffect);
+  }
+};
+/// This trait indicates that the side effects of an operation includes the
+/// effects of operations nested within its regions. If the operation has no
+/// derived effects interfaces, the operation itself can be assumed to have no
+/// side effects.
+template <typename ConcreteType>
+class HasRecursiveSideEffects
+    : public TraitBase<ConcreteType, HasRecursiveSideEffects> {};
+} // namespace OpTrait
+
+//===----------------------------------------------------------------------===//
+// Operation Memory-Effect Modeling
+//===----------------------------------------------------------------------===//
+
+namespace MemoryEffects {
+/// This class represents the base class used for memory effects.
+struct Effect : public SideEffects::Effect {
+  using SideEffects::Effect::Effect;
+
+  /// A base class for memory effects that provides helper utilities.
+  template <typename DerivedEffect>
+  using Base = SideEffects::Effect::Base<DerivedEffect, Effect>;
+
+  static bool classof(const SideEffects::Effect *effect);
+};
+using EffectInstance = SideEffects::EffectInstance<Effect>;
+
+/// The following effect indicates that the operation allocates from some
+/// resource. An 'allocate' effect implies only allocation of the resource, and
+/// not any visible mutation or dereference.
+struct Allocate : public Effect::Base<Allocate> {};
+
+/// The following effect indicates that the operation frees some resource that
+/// has been allocated. An 'allocate' effect implies only de-allocation of the
+/// resource, and not any visible allocation, mutation or dereference.
+struct Free : public Effect::Base<Free> {};
+
+/// The following effect indicates that the operation reads from some resource.
+/// A 'read' effect implies only dereferencing of the resource, and not any
+/// visible mutation.
+struct Read : public Effect::Base<Read> {};
+
+/// The following effect indicates that the operation writes to some resource. A
+/// 'write' effect implies only mutating a resource, and not any visible
+/// dereference or read.
+struct Write : public Effect::Base<Write> {};
+} // namespace MemoryEffects
+
+//===----------------------------------------------------------------------===//
+// SideEffect Interfaces
+
+/// Include the definitions of the side effect interfaces.
+#include "mlir/IR/SideEffectInterfaces.h.inc"
+
+//===----------------------------------------------------------------------===//
+// Common Operation Folders/Parsers/Printers
+//===----------------------------------------------------------------------===//
+
 // These functions are out-of-line implementations of the methods in UnaryOp and
 // BinaryOp, which avoids them being template instantiated/duplicated.
 namespace impl {

diff  --git a/mlir/include/mlir/IR/SideEffects.td b/mlir/include/mlir/IR/SideEffects.td
new file mode 100644
index 000000000000..9d06348c98b7
--- /dev/null
+++ b/mlir/include/mlir/IR/SideEffects.td
@@ -0,0 +1,191 @@
+//===-- SideEffects.td - Side Effect Interfaces ------------*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains a set of interfaces that can be used to define information
+// about what effects are applied by an operation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_IR_SIDEEFFECTS
+#define MLIR_IR_SIDEEFFECTS
+
+include "mlir/IR/OpBase.td"
+
+//===----------------------------------------------------------------------===//
+// EffectOpInterface
+//===----------------------------------------------------------------------===//
+
+// A base interface used to query information about the side effects applied to
+// an operation. This template class takes the name of the derived interface
+// class, as well as the name of the base effect class.
+class EffectOpInterfaceBase<string name, string baseEffect>
+    : OpInterface<name> {
+  let methods = [
+    InterfaceMethod<[{
+        Collects all of the operation's effects into `effects`.
+      }],
+      "void", "getEffects",
+         (ins "SmallVectorImpl<SideEffects::EffectInstance<"
+              # baseEffect # ">> &":$effects)
+    >,
+    InterfaceMethod<[{
+        Collects all of the operation's effects into `effects`.
+      }],
+      "void", "getEffectsOnValue",
+         (ins "Value":$value,
+              "SmallVectorImpl<SideEffects::EffectInstance<"
+              # baseEffect # ">> &":$effects), [{
+          op.getEffects(effects);
+          llvm::erase_if(effects, [&](auto &it) {
+            return it.getValue() != value;
+          });
+       }]
+    >,
+    InterfaceMethod<[{
+        Collects all of the effects that are exhibited by this operation on the
+        given resource and place them in 'effects'.
+      }],
+      "void", "getEffectsOnResource",
+         (ins "SideEffects::Resource *":$resource,
+              "SmallVectorImpl<SideEffects::EffectInstance<"
+              # baseEffect # ">> &":$effects), [{
+          op.getEffects(effects);
+          llvm::erase_if(effects, [&](auto &it) {
+            return it.getResource() != resource;
+          });
+       }]
+    >
+  ];
+
+  let extraClassDeclaration = [{
+    /// Collect all of the effect instances that correspond to the given
+    /// `Effect` and place them in 'effects'.
+    template <typename Effect> void getEffects(
+      SmallVectorImpl<SideEffects::EffectInstance<
+                                              }] # baseEffect # [{>> &effects) {
+      getEffects(effects);
+      llvm::erase_if(effects, [&](auto &it) {
+        return !llvm::isa<Effect>(it.getEffect());
+      });
+    }
+
+    /// Returns true if this operation exhibits the given effect.
+    template <typename Effect> bool hasEffect() {
+      SmallVector<SideEffects::EffectInstance<}] # baseEffect # [{>, 4> effects;
+      getEffects(effects);
+      return llvm::any_of(effects, [](const auto &it) {
+        return llvm::isa<Effect>(it.getEffect());
+      });
+    }
+
+    /// Returns if this operation only has the given effect.
+    template <typename Effect> bool onlyHasEffect() {
+      SmallVector<SideEffects::EffectInstance<}] # baseEffect # [{>, 4> effects;
+      getEffects(effects);
+      return !effects.empty() && llvm::all_of(effects, [](const auto &it) {
+        return isa<Effect>(it.getEffect());
+      });
+    }
+
+    /// Returns if this operation has no effects.
+    bool hasNoEffect() {
+      SmallVector<SideEffects::EffectInstance<}] # baseEffect # [{>, 4> effects;
+      getEffects(effects);
+      return effects.empty();
+    }
+  }];
+
+  // The base effect name of this interface.
+  string baseEffectName = baseEffect;
+}
+
+// This class is the general base side effect class. This is used by derived
+// effect interfaces to define their effects.
+class SideEffect<EffectOpInterfaceBase interface, string effectName,
+                 string resourceName> {
+  /// The parent interface that the effect belongs to.
+  string interfaceTrait = interface.trait;
+
+  /// The name of the base effects class.
+  string baseEffect = interface.baseEffectName;
+
+  /// The derived effect that is being applied.
+  string effect = effectName;
+
+  /// The resource that the effect is being applied to.
+  string resource = resourceName;
+}
+
+// This class is the base used for specifying effects applied to an operation.
+class SideEffectsTraitBase<EffectOpInterfaceBase parentInterface,
+                           list<SideEffect> staticEffects>
+    : OpInterfaceTrait<""> {
+  /// The name of the interface trait to use.
+  let trait = parentInterface.trait;
+
+  /// The derived effects being applied.
+  list<SideEffect> effects = staticEffects;
+}
+
+//===----------------------------------------------------------------------===//
+// MemoryEffects
+//===----------------------------------------------------------------------===//
+
+// This def represents the definition for the memory effects interface. Users
+// should generally not use this directly, and should instead use
+// `MemoryEffects`.
+def MemoryEffectsOpInterface
+    : EffectOpInterfaceBase<"MemoryEffectOpInterface",
+                            "MemoryEffects::Effect"> {
+  let description = [{
+    An interface used to query information about the memory effects applied by
+    an operation.
+  }];
+}
+
+// The base class for defining specific memory effects.
+class MemoryEffect<string effectName, string resourceName>
+  : SideEffect<MemoryEffectsOpInterface, effectName, resourceName>;
+
+// This class represents the trait for memory effects that may be placed on
+// operations.
+class MemoryEffects<list<MemoryEffect> effects = []>
+  : SideEffectsTraitBase<MemoryEffectsOpInterface, effects>;
+
+//===----------------------------------------------------------------------===//
+// Effects
+
+// The following effect indicates that the operation allocates from some
+// resource. An 'allocate' effect implies only allocation of the resource, and
+// not any visible mutation or dereference.
+class MemAlloc<string resourceName>
+  : MemoryEffect<"MemoryEffects::Allocate", resourceName>;
+def MemAlloc : MemAlloc<"">;
+
+// The following effect indicates that the operation frees some resource that
+// has been allocated. A 'free' effect implies only de-allocation of the
+// resource, and not any visible allocation, mutation or dereference.
+class MemFree<string resourceName>
+  : MemoryEffect<"MemoryEffects::Free", resourceName>;
+def MemFree : MemFree<"">;
+
+// The following effect indicates that the operation reads from some
+// resource. A 'read' effect implies only dereferencing of the resource, and
+// not any visible mutation.
+class MemRead<string resourceName>
+  : MemoryEffect<"MemoryEffects::Read", resourceName>;
+def MemRead : MemRead<"">;
+
+// The following effect indicates that the operation writes to some
+// resource. A 'write' effect implies only mutating a resource, and not any
+// visible dereference or read.
+class MemWrite<string resourceName>
+  : MemoryEffect<"MemoryEffects::Write", resourceName>;
+def MemWrite : MemWrite<"">;
+
+#endif // MLIR_IR_SIDEEFFECTS

diff  --git a/mlir/lib/IR/CMakeLists.txt b/mlir/lib/IR/CMakeLists.txt
index 6b9c17a4d512..3f7a42231fe2 100644
--- a/mlir/lib/IR/CMakeLists.txt
+++ b/mlir/lib/IR/CMakeLists.txt
@@ -7,6 +7,7 @@ add_mlir_library(MLIRIR
 
   DEPENDS
   MLIRCallOpInterfacesIncGen
+  MLIRSideEffectOpInterfacesIncGen
   MLIROpAsmInterfacesIncGen
   )
 target_link_libraries(MLIRIR

diff  --git a/mlir/lib/IR/Operation.cpp b/mlir/lib/IR/Operation.cpp
index 0d207fd1a6aa..451d7415154f 100644
--- a/mlir/lib/IR/Operation.cpp
+++ b/mlir/lib/IR/Operation.cpp
@@ -957,6 +957,17 @@ LogicalResult OpTrait::impl::verifyResultSizeAttr(Operation *op,
   return verifyValueSizeAttr(op, attrName, /*isOperand=*/false);
 }
 
+//===----------------------------------------------------------------------===//
+// SideEffect Interfaces
+
+/// Include the definitions of the side effect interfaces.
+#include "mlir/IR/SideEffectInterfaces.cpp.inc"
+
+bool MemoryEffects::Effect::classof(const SideEffects::Effect *effect) {
+  return isa<Allocate>(effect) || isa<Free>(effect) || isa<Read>(effect) ||
+         isa<Write>(effect);
+}
+
 //===----------------------------------------------------------------------===//
 // BinaryOp implementation
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Translation/CMakeLists.txt b/mlir/lib/Translation/CMakeLists.txt
index 8ceebd5d0633..8b999d26987d 100644
--- a/mlir/lib/Translation/CMakeLists.txt
+++ b/mlir/lib/Translation/CMakeLists.txt
@@ -7,4 +7,5 @@ add_mlir_library(MLIRTranslation
 target_link_libraries(MLIRTranslation
   PUBLIC
   LLVMSupport
+  MLIRIR
   )

diff  --git a/mlir/test/IR/test-side-effects.mlir b/mlir/test/IR/test-side-effects.mlir
new file mode 100644
index 000000000000..6832483ff353
--- /dev/null
+++ b/mlir/test/IR/test-side-effects.mlir
@@ -0,0 +1,20 @@
+// RUN: mlir-opt %s -test-side-effects -verify-diagnostics
+
+// expected-remark at +1 {{operation has no memory effects}}
+%0 = "test.side_effect_op"() {} : () -> i32
+
+// expected-remark at +2 {{found an instance of 'read' on resource '<Default>'}}
+// expected-remark at +1 {{found an instance of 'free' on resource '<Default>'}}
+%1 = "test.side_effect_op"() {effects = [
+  {effect="read"}, {effect="free"}
+]} : () -> i32
+
+// expected-remark at +1 {{found an instance of 'write' on resource '<Test>'}}
+%2 = "test.side_effect_op"() {effects = [
+  {effect="write", test_resource}
+]} : () -> i32
+
+// expected-remark at +1 {{found an instance of 'allocate' on a value, on resource '<Test>'}}
+%3 = "test.side_effect_op"() {effects = [
+  {effect="allocate", on_result, test_resource}
+]} : () -> i32

diff  --git a/mlir/test/lib/IR/CMakeLists.txt b/mlir/test/lib/IR/CMakeLists.txt
index c10dc157edd9..945a0f5efdcb 100644
--- a/mlir/test/lib/IR/CMakeLists.txt
+++ b/mlir/test/lib/IR/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_llvm_library(MLIRTestIR
   TestFunc.cpp
   TestMatchers.cpp
+  TestSideEffects.cpp
   TestSymbolUses.cpp
 
   ADDITIONAL_HEADER_DIRS

diff  --git a/mlir/test/lib/IR/TestSideEffects.cpp b/mlir/test/lib/IR/TestSideEffects.cpp
new file mode 100644
index 000000000000..9f52c42e4953
--- /dev/null
+++ b/mlir/test/lib/IR/TestSideEffects.cpp
@@ -0,0 +1,58 @@
+//===- TestSidEffects.cpp - Pass to test side effects ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestDialect.h"
+#include "mlir/Pass/Pass.h"
+
+using namespace mlir;
+
+namespace {
+struct SideEffectsPass : public ModulePass<SideEffectsPass> {
+  void runOnModule() override {
+    auto module = getModule();
+
+    // Walk operations detecting side effects.
+    SmallVector<MemoryEffects::EffectInstance, 8> effects;
+    module.walk([&](MemoryEffectOpInterface op) {
+      effects.clear();
+      op.getEffects(effects);
+
+      // Check to see if this operation has any memory effects.
+      if (effects.empty()) {
+        op.emitRemark() << "operation has no memory effects";
+        return;
+      }
+
+      for (MemoryEffects::EffectInstance instance : effects) {
+        auto diag = op.emitRemark() << "found an instance of ";
+
+        if (isa<MemoryEffects::Allocate>(instance.getEffect()))
+          diag << "'allocate'";
+        else if (isa<MemoryEffects::Free>(instance.getEffect()))
+          diag << "'free'";
+        else if (isa<MemoryEffects::Read>(instance.getEffect()))
+          diag << "'read'";
+        else if (isa<MemoryEffects::Write>(instance.getEffect()))
+          diag << "'write'";
+
+        if (instance.getValue())
+          diag << " on a value,";
+
+        diag << " on resource '" << instance.getResource()->getName() << "'";
+      }
+    });
+  }
+};
+} // end anonymous namespace
+
+namespace mlir {
+void registerSideEffectTestPasses() {
+  PassRegistration<SideEffectsPass>("test-side-effects",
+                                    "Test side effects interfaces");
+}
+} // namespace mlir

diff  --git a/mlir/test/lib/TestDialect/TestDialect.cpp b/mlir/test/lib/TestDialect/TestDialect.cpp
index 649b547626d9..94b5017978a0 100644
--- a/mlir/test/lib/TestDialect/TestDialect.cpp
+++ b/mlir/test/lib/TestDialect/TestDialect.cpp
@@ -345,6 +345,56 @@ LogicalResult OpWithShapedTypeInferTypeInterfaceOp::reifyReturnTypeShapes(
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// Test SideEffect interfaces
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// A test resource for side effects.
+struct TestResource : public SideEffects::Resource::Base<TestResource> {
+  StringRef getName() final { return "<Test>"; }
+};
+} // end anonymous namespace
+
+void SideEffectOp::getEffects(
+    SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
+  // Check for an effects attribute on the op instance.
+  ArrayAttr effectsAttr = getAttrOfType<ArrayAttr>("effects");
+  if (!effectsAttr)
+    return;
+
+  // If there is one, it is an array of dictionary attributes that hold
+  // information on the effects of this operation.
+  for (Attribute element : effectsAttr) {
+    DictionaryAttr effectElement = element.cast<DictionaryAttr>();
+
+    // Get the specific memory effect.
+    MemoryEffects::Effect *effect =
+        llvm::StringSwitch<MemoryEffects::Effect *>(
+            effectElement.get("effect").cast<StringAttr>().getValue())
+            .Case("allocate", MemoryEffects::Allocate::get())
+            .Case("free", MemoryEffects::Free::get())
+            .Case("read", MemoryEffects::Read::get())
+            .Case("write", MemoryEffects::Write::get());
+
+    // Check for a result to affect.
+    Value value;
+    if (effectElement.get("on_result"))
+      value = getResult();
+
+    // Check for a non-default resource to use.
+    SideEffects::Resource *resource = SideEffects::DefaultResource::get();
+    if (effectElement.get("test_resource"))
+      resource = TestResource::get();
+
+    effects.emplace_back(effect, value, resource);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Dialect Registration
+//===----------------------------------------------------------------------===//
+
 // Static initialization for Test dialect registration.
 static mlir::DialectRegistration<mlir::TestDialect> testDialect;
 

diff  --git a/mlir/test/lib/TestDialect/TestOps.td b/mlir/test/lib/TestDialect/TestOps.td
index 904003e1461c..88c830740101 100644
--- a/mlir/test/lib/TestDialect/TestOps.td
+++ b/mlir/test/lib/TestDialect/TestOps.td
@@ -11,6 +11,7 @@
 
 include "mlir/IR/OpBase.td"
 include "mlir/IR/OpAsmInterface.td"
+include "mlir/IR/SideEffects.td"
 include "mlir/Analysis/ControlFlowInterfaces.td"
 include "mlir/Analysis/CallInterfaces.td"
 include "mlir/Analysis/InferTypeOpInterface.td"
@@ -1176,4 +1177,13 @@ def FormatSuccessorAOp : TEST_Op<"format_successor_a_op", [Terminator]> {
   let assemblyFormat = "$targets attr-dict";
 }
 
+//===----------------------------------------------------------------------===//
+// Test SideEffects
+//===----------------------------------------------------------------------===//
+
+def SideEffectOp : TEST_Op<"side_effect_op",
+    [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
+  let results = (outs AnyType:$result);
+}
+
 #endif // TEST_OPS

diff  --git a/mlir/tools/mlir-opt/mlir-opt.cpp b/mlir/tools/mlir-opt/mlir-opt.cpp
index b344056a0f19..443d951573e1 100644
--- a/mlir/tools/mlir-opt/mlir-opt.cpp
+++ b/mlir/tools/mlir-opt/mlir-opt.cpp
@@ -35,6 +35,7 @@ void registerMemRefBoundCheck();
 void registerPassManagerTestPass();
 void registerPatternsTestPass();
 void registerPrintOpAvailabilityPass();
+void registerSideEffectTestPasses();
 void registerSimpleParametricTilingPass();
 void registerSymbolTestPasses();
 void registerTestAffineDataCopyPass();
@@ -89,6 +90,7 @@ void registerTestPasses() {
   registerPassManagerTestPass();
   registerPatternsTestPass();
   registerPrintOpAvailabilityPass();
+  registerSideEffectTestPasses();
   registerSimpleParametricTilingPass();
   registerSymbolTestPasses();
   registerTestAffineDataCopyPass();


        


More information about the Mlir-commits mailing list