| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> : OpVariableDecorator { | ||
| /// 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| //===- SideEffects.h - Side Effects classes ---------------------*- C++ -*-===// | ||
| // | ||
| // Part of the MLIR 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Wrapper around side effect related classes defined in TableGen. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef MLIR_TABLEGEN_SIDEEFFECTS_H_ | ||
| #define MLIR_TABLEGEN_SIDEEFFECTS_H_ | ||
|
|
||
| #include "mlir/Support/LLVM.h" | ||
| #include "mlir/TableGen/Operator.h" | ||
|
|
||
| namespace mlir { | ||
| namespace tblgen { | ||
|
|
||
| // This class represents a specific instance of an effect that is being | ||
| // exhibited. | ||
| class SideEffect : public Operator::VariableDecorator { | ||
| public: | ||
| // Return the name of the C++ effect. | ||
| StringRef getName() const; | ||
|
|
||
| // Return the name of the base C++ effect. | ||
| StringRef getBaseName() const; | ||
|
|
||
| // Return the name of the parent interface trait. | ||
| StringRef getInterfaceTrait() const; | ||
|
|
||
| // Return the name of the resource class. | ||
| StringRef getResource() const; | ||
|
|
||
| static bool classof(const Operator::VariableDecorator *var); | ||
| }; | ||
|
|
||
| // This class represents an instance of a side effect interface applied to an | ||
| // operation. This is a wrapper around an OpInterfaceTrait that also includes | ||
| // the effects that are applied. | ||
| class SideEffectTrait : public InterfaceOpTrait { | ||
| public: | ||
| // Return the effects that are attached to the side effect interface. | ||
| Operator::var_decorator_range getEffects() const; | ||
|
|
||
| static bool classof(const OpTrait *t); | ||
| }; | ||
|
|
||
| } // end namespace tblgen | ||
| } // end namespace mlir | ||
|
|
||
| #endif // MLIR_TABLEGEN_SIDEEFFECTS_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| //===- SideEffects.cpp - SideEffect classes -------------------------------===// | ||
| // | ||
| // Part of the MLIR 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 "mlir/TableGen/SideEffects.h" | ||
| #include "llvm/TableGen/Record.h" | ||
|
|
||
| using namespace mlir; | ||
| using namespace mlir::tblgen; | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // SideEffect | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| StringRef SideEffect::getName() const { | ||
| return def->getValueAsString("effect"); | ||
| } | ||
|
|
||
| StringRef SideEffect::getBaseName() const { | ||
| return def->getValueAsString("baseEffect"); | ||
| } | ||
|
|
||
| StringRef SideEffect::getInterfaceTrait() const { | ||
| return def->getValueAsString("interfaceTrait"); | ||
| } | ||
|
|
||
| StringRef SideEffect::getResource() const { | ||
| auto value = def->getValueAsString("resource"); | ||
| return value.empty() ? "::mlir::SideEffects::DefaultResource" : value; | ||
| } | ||
|
|
||
| bool SideEffect::classof(const Operator::VariableDecorator *var) { | ||
| return var->getDef().isSubClassOf("SideEffect"); | ||
| } | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // SideEffectsTrait | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| Operator::var_decorator_range SideEffectTrait::getEffects() const { | ||
| auto *listInit = dyn_cast<llvm::ListInit>(def->getValueInit("effects")); | ||
| return {listInit->begin(), listInit->end()}; | ||
| } | ||
|
|
||
| bool SideEffectTrait::classof(const OpTrait *t) { | ||
| return t->getDef().isSubClassOf("SideEffectsTraitBase"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,4 +7,5 @@ add_mlir_library(MLIRTranslation | |
| target_link_libraries(MLIRTranslation | ||
| PUBLIC | ||
| LLVMSupport | ||
| MLIRIR | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // RUN: mlir-opt %s -test-side-effects -verify-diagnostics | ||
|
|
||
| // expected-remark@+1 {{operation has no memory effects}} | ||
| %0 = "test.side_effect_op"() {} : () -> i32 | ||
|
|
||
| // expected-remark@+2 {{found an instance of 'read' on resource '<Default>'}} | ||
| // expected-remark@+1 {{found an instance of 'free' on resource '<Default>'}} | ||
| %1 = "test.side_effect_op"() {effects = [ | ||
| {effect="read"}, {effect="free"} | ||
| ]} : () -> i32 | ||
|
|
||
| // expected-remark@+1 {{found an instance of 'write' on resource '<Test>'}} | ||
| %2 = "test.side_effect_op"() {effects = [ | ||
| {effect="write", test_resource} | ||
| ]} : () -> i32 | ||
|
|
||
| // expected-remark@+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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // RUN: mlir-tblgen -gen-op-defs -I %S/../../include %s | FileCheck %s | ||
|
|
||
| include "mlir/IR/SideEffects.td" | ||
|
|
||
| def TEST_Dialect : Dialect { | ||
| let name = "test"; | ||
| } | ||
| class TEST_Op<string mnemonic, list<OpTrait> traits = []> : | ||
| Op<TEST_Dialect, mnemonic, traits>; | ||
|
|
||
| def SideEffectOpA : TEST_Op<"side_effect_op_a"> { | ||
| let arguments = (ins Arg<Variadic<AnyMemRef>, "", [MemRead]>); | ||
| let results = (outs Res<AnyMemRef, "", [MemAlloc<"CustomResource">]>); | ||
| } | ||
|
|
||
| def SideEffectOpB : TEST_Op<"side_effect_op_b", | ||
| [MemoryEffects<[MemWrite<"CustomResource">]>]>; | ||
|
|
||
| // CHECK: void SideEffectOpA::getEffects | ||
| // CHECK: for (Value value : getODSOperands(0)) | ||
| // CHECK: effects.emplace_back(MemoryEffects::Read::get(), value, ::mlir::SideEffects::DefaultResource::get()); | ||
| // CHECK: for (Value value : getODSResults(0)) | ||
| // CHECK: effects.emplace_back(MemoryEffects::Allocate::get(), value, CustomResource::get()); | ||
|
|
||
| // CHECK: void SideEffectOpB::getEffects | ||
| // CHECK: effects.emplace_back(MemoryEffects::Write::get(), CustomResource::get()); |