Skip to content

Commit

Permalink
[mlir][SideEffects] Define a set of interfaces and traits for definin…
Browse files Browse the repository at this point in the history
…g 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
  • Loading branch information
River707 committed Mar 6, 2020
1 parent 7b8341b commit f892358
Show file tree
Hide file tree
Showing 13 changed files with 568 additions and 10 deletions.
5 changes: 5 additions & 0 deletions mlir/include/mlir/IR/CMakeLists.txt
Expand Up @@ -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)
2 changes: 2 additions & 0 deletions mlir/include/mlir/IR/OpBase.td
Expand Up @@ -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.
Expand Down
226 changes: 216 additions & 10 deletions mlir/include/mlir/IR/OpDefinition.h
Expand Up @@ -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>
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit f892358

Please sign in to comment.