Skip to content

Commit 0ddba0b

Browse files
committed
[mlir][SideEffects] Replace HasNoSideEffect with the memory effect interfaces.
HasNoSideEffect can now be implemented using the MemoryEffectInterface, removing the need to check multiple things for the same information. This also removes an easy foot-gun for users as 'Operation::hasNoSideEffect' would ignore operations that dynamically, or recursively, have no side effects. This also leads to an immediate improvement in some of the existing users, such as DCE, now that they have access to more information. Differential Revision: https://reviews.llvm.org/D76036
1 parent 907403f commit 0ddba0b

File tree

23 files changed

+180
-107
lines changed

23 files changed

+180
-107
lines changed

mlir/docs/Traits.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,6 @@ foo.region_op {
208208
This trait is an important structural property of the IR, and enables operations
209209
to have [passes](WritingAPass.md) scheduled under them.
210210

211-
### NoSideEffect
212-
213-
* `OpTrait::HasNoSideEffect` -- `NoSideEffect`
214-
215-
This trait signifies that the operation is pure and has no visible side effects.
216-
217211
### Single Block with Implicit Terminator
218212

219213
* `OpTrait::SingleBlockImplicitTerminator<typename TerminatorOpType>` :

mlir/docs/Tutorials/Toy/Ch-2.md

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,7 @@ class ConstantOp : public mlir::Op<ConstantOp,
206206
/// The ConstantOp takes no inputs.
207207
mlir::OpTrait::ZeroOperands,
208208
/// The ConstantOp returns a single result.
209-
mlir::OpTrait::OneResult,
210-
/// The ConstantOp is pure and has no visible side-effects.
211-
mlir::OpTrait::HasNoSideEffect> {
209+
mlir::OpTrait::OneResult> {
212210

213211
public:
214212
/// Inherit the constructors from the base Op class.
@@ -335,15 +333,13 @@ operation.
335333
We define a toy operation by inheriting from our base 'Toy_Op' class above. Here
336334
we provide the mnemonic and a list of traits for the operation. The
337335
[mnemonic](../../OpDefinitions.md#operation-name) here matches the one given in
338-
`ConstantOp::getOperationName` without the dialect prefix; `toy.`. The constant
339-
operation here is also marked as 'NoSideEffect'. This is an ODS trait, and
340-
matches one-to-one with the trait we providing when defining `ConstantOp`:
341-
`mlir::OpTrait::HasNoSideEffect`. Missing here from our C++ definition are the
342-
`ZeroOperands` and `OneResult` traits; these will be automatically inferred
343-
based upon the `arguments` and `results` fields we define later.
336+
`ConstantOp::getOperationName` without the dialect prefix; `toy.`. Missing here
337+
from our C++ definition are the `ZeroOperands` and `OneResult` traits; these
338+
will be automatically inferred based upon the `arguments` and `results` fields
339+
we define later.
344340
345341
```tablegen
346-
def ConstantOp : Toy_Op<"constant", [NoSideEffect]> {
342+
def ConstantOp : Toy_Op<"constant"> {
347343
}
348344
```
349345
@@ -369,7 +365,7 @@ values. The results correspond to a set of types for the values produced by the
369365
operation:
370366
371367
```tablegen
372-
def ConstantOp : Toy_Op<"constant", [NoSideEffect]> {
368+
def ConstantOp : Toy_Op<"constant"> {
373369
// The constant operation takes an attribute as the only input.
374370
// `F64ElementsAttr` corresponds to a 64-bit floating-point ElementsAttr.
375371
let arguments = (ins F64ElementsAttr:$value);
@@ -394,7 +390,7 @@ for users of the dialect and can even be used to auto-generate Markdown
394390
documents.
395391
396392
```tablegen
397-
def ConstantOp : Toy_Op<"constant", [NoSideEffect]> {
393+
def ConstantOp : Toy_Op<"constant"> {
398394
// Provide a summary and description for this operation. This can be used to
399395
// auto-generate documentation of the operations within our dialect.
400396
let summary = "constant operation";
@@ -432,7 +428,7 @@ as part of `ConstantOp::verify`. This blob can assume that all of the other
432428
invariants of the operation have already been verified:
433429
434430
```tablegen
435-
def ConstantOp : Toy_Op<"constant", [NoSideEffect]> {
431+
def ConstantOp : Toy_Op<"constant"> {
436432
// Provide a summary and description for this operation. This can be used to
437433
// auto-generate documentation of the operations within our dialect.
438434
let summary = "constant operation";
@@ -472,7 +468,7 @@ of C++ parameters, as well as an optional code block that can be used to specify
472468
the implementation inline.
473469
474470
```tablegen
475-
def ConstantOp : Toy_Op<"constant", [NoSideEffect]> {
471+
def ConstantOp : Toy_Op<"constant"> {
476472
...
477473
478474
// Add custom build methods for the constant operation. These methods populate

mlir/include/mlir/IR/Operation.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -407,13 +407,6 @@ class Operation final
407407
return false;
408408
}
409409

410-
/// Returns whether the operation has side-effects.
411-
bool hasNoSideEffect() {
412-
if (auto *absOp = getAbstractOperation())
413-
return absOp->hasProperty(OperationProperty::NoSideEffect);
414-
return false;
415-
}
416-
417410
/// Represents the status of whether an operation is a terminator. We
418411
/// represent an 'unknown' status because we want to support unregistered
419412
/// terminators.

mlir/include/mlir/IR/OperationSupport.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,15 @@ enum class OperationProperty {
6868
/// results.
6969
Commutative = 0x1,
7070

71-
/// This bit is set for operations that have no side effects: that means that
72-
/// they do not read or write memory, or access any hidden state.
73-
NoSideEffect = 0x2,
74-
7571
/// This bit is set for an operation if it is a terminator: that means
7672
/// an operation at the end of a block.
77-
Terminator = 0x4,
73+
Terminator = 0x2,
7874

7975
/// This bit is set for operations that are completely isolated from above.
8076
/// This is used for operations whose regions are explicit capture only, i.e.
8177
/// they are never allowed to implicitly reference values defined above the
8278
/// parent operation.
83-
IsolatedFromAbove = 0x8,
79+
IsolatedFromAbove = 0x4,
8480
};
8581

8682
/// This is a "type erased" representation of a registered operation. This

mlir/include/mlir/Interfaces/SideEffects.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,6 @@ template <typename EffectT> class EffectInstance {
163163
//===----------------------------------------------------------------------===//
164164

165165
namespace OpTrait {
166-
/// This trait indicates that an operation never has side effects.
167-
template <typename ConcreteType>
168-
class HasNoSideEffect : public TraitBase<ConcreteType, HasNoSideEffect> {
169-
public:
170-
static AbstractOperation::OperationProperties getTraitProperties() {
171-
return static_cast<AbstractOperation::OperationProperties>(
172-
OperationProperty::NoSideEffect);
173-
}
174-
};
175166
/// This trait indicates that the side effects of an operation includes the
176167
/// effects of operations nested within its regions. If the operation has no
177168
/// derived effects interfaces, the operation itself can be assumed to have no
@@ -221,10 +212,24 @@ struct Write : public Effect::Base<Write> {};
221212

222213
//===----------------------------------------------------------------------===//
223214
// SideEffect Interfaces
215+
//===----------------------------------------------------------------------===//
224216

225217
/// Include the definitions of the side effect interfaces.
226218
#include "mlir/Interfaces/SideEffectInterfaces.h.inc"
227219

220+
//===----------------------------------------------------------------------===//
221+
// SideEffect Utilities
222+
//===----------------------------------------------------------------------===//
223+
224+
/// Return true if the given operation is unused, and has no side effects on
225+
/// memory that prevent erasing.
226+
bool isOpTriviallyDead(Operation *op);
227+
228+
/// Return true if the given operation would be dead if unused, and has no side
229+
/// effects on memory that would prevent erasing. This is equivalent to checking
230+
/// `isOpTriviallyDead` if `op` was unused.
231+
bool wouldOpBeTriviallyDead(Operation *op);
232+
228233
} // end namespace mlir
229234

230235
#endif // MLIR_INTERFACES_SIDEEFFECTS_H

mlir/include/mlir/Interfaces/SideEffects.td

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ class EffectOpInterfaceBase<string name, string baseEffect>
9898
getEffects(effects);
9999
return effects.empty();
100100
}
101+
102+
/// Returns if the given operation has no effects for this interface.
103+
static bool hasNoEffect(Operation *op) {
104+
if (auto interface = dyn_cast<}] # name # [{>(op))
105+
return interface.hasNoEffect();
106+
return op->hasTrait<OpTrait::HasRecursiveSideEffects>();
107+
}
101108
}];
102109

103110
// The base effect name of this interface.
@@ -108,11 +115,8 @@ class EffectOpInterfaceBase<string name, string baseEffect>
108115
// effect interfaces to define their effects.
109116
class SideEffect<EffectOpInterfaceBase interface, string effectName,
110117
string resourceName> : OpVariableDecorator {
111-
/// The parent interface that the effect belongs to.
112-
string interfaceTrait = interface.trait;
113-
114118
/// The name of the base effects class.
115-
string baseEffect = interface.baseEffectName;
119+
string baseEffectName = interface.baseEffectName;
116120

117121
/// The derived effect that is being applied.
118122
string effect = effectName;
@@ -128,6 +132,9 @@ class SideEffectsTraitBase<EffectOpInterfaceBase parentInterface,
128132
/// The name of the interface trait to use.
129133
let trait = parentInterface.trait;
130134

135+
/// The name of the base effects class.
136+
string baseEffectName = parentInterface.baseEffectName;
137+
131138
/// The derived effects being applied.
132139
list<SideEffect> effects = staticEffects;
133140
}
@@ -193,7 +200,7 @@ def MemWrite : MemWrite<"">;
193200
//===----------------------------------------------------------------------===//
194201

195202
// Op has no side effect.
196-
def NoSideEffect : NativeOpTrait<"HasNoSideEffect">;
203+
def NoSideEffect : MemoryEffects<[]>;
197204
// Op has recursively computed side effects.
198205
def RecursiveSideEffects : NativeOpTrait<"HasRecursiveSideEffects">;
199206

mlir/include/mlir/TableGen/SideEffects.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ class SideEffect : public Operator::VariableDecorator {
2727
StringRef getName() const;
2828

2929
// Return the name of the base C++ effect.
30-
StringRef getBaseName() const;
31-
32-
// Return the name of the parent interface trait.
33-
StringRef getInterfaceTrait() const;
30+
StringRef getBaseEffectName() const;
3431

3532
// Return the name of the resource class.
3633
StringRef getResource() const;
@@ -46,6 +43,9 @@ class SideEffectTrait : public InterfaceOpTrait {
4643
// Return the effects that are attached to the side effect interface.
4744
Operator::var_decorator_range getEffects() const;
4845

46+
// Return the name of the base C++ effect.
47+
StringRef getBaseEffectName() const;
48+
4949
static bool classof(const OpTrait *t);
5050
};
5151

mlir/include/mlir/Transforms/FoldUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ class OperationFolder {
106106
return op;
107107
}
108108

109+
/// Clear out any constants cached inside of the folder.
110+
void clear();
111+
109112
private:
110113
/// This map keeps track of uniqued constants by dialect, attribute, and type.
111114
/// A constant operation materializes an attribute with a type. Dialects may

mlir/lib/Analysis/Utils.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -974,11 +974,12 @@ void mlir::getSequentialLoops(AffineForOp forOp,
974974
bool mlir::isLoopParallel(AffineForOp forOp) {
975975
// Collect all load and store ops in loop nest rooted at 'forOp'.
976976
SmallVector<Operation *, 8> loadAndStoreOpInsts;
977-
auto walkResult = forOp.walk([&](Operation *opInst) {
977+
auto walkResult = forOp.walk([&](Operation *opInst) -> WalkResult {
978978
if (isa<AffineLoadOp>(opInst) || isa<AffineStoreOp>(opInst))
979979
loadAndStoreOpInsts.push_back(opInst);
980980
else if (!isa<AffineForOp>(opInst) && !isa<AffineTerminatorOp>(opInst) &&
981-
!isa<AffineIfOp>(opInst) && !opInst->hasNoSideEffect())
981+
!isa<AffineIfOp>(opInst) &&
982+
!MemoryEffectOpInterface::hasNoEffect(opInst))
982983
return WalkResult::interrupt();
983984

984985
return WalkResult::advance();

mlir/lib/Conversion/LoopsToGPU/LoopsToGPU.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,9 @@ ParallelToGpuLaunchLowering::matchAndRewrite(ParallelOp parallelOp,
797797
Operation *clone = rewriter.clone(*op, cloningMap);
798798
cloningMap.map(op->getResults(), clone->getResults());
799799
// Check for side effects.
800-
seenSideeffects |= !clone->hasNoSideEffect();
800+
// TODO: Handle region side effects properly.
801+
seenSideeffects |= !MemoryEffectOpInterface::hasNoEffect(clone) ||
802+
clone->getNumRegions() != 0;
801803
// If we are no longer in the innermost scope, sideeffects are disallowed.
802804
if (seenSideeffects && leftNestingScope)
803805
return matchFailure();

0 commit comments

Comments
 (0)