Skip to content

Commit 0499d3a

Browse files
[mlir][Interfaces] Add hasUnknownEffects helper function (#154523)
I have seen misuse of the `hasEffect` API in downstream projects: users sometimes think that `hasEffect == false` indicates that the operation does not have a certain memory effect. That's not necessarily the case. When the op does not implement the `MemoryEffectsOpInterface`, it is unknown whether it has the specified effect. "false" can also mean "maybe". This commit clarifies the semantics in the documentation. Also adds `hasUnknownEffects` and `mightHaveEffect` convenience functions. Also simplifies a few call sites.
1 parent 2e2349e commit 0499d3a

File tree

4 files changed

+60
-27
lines changed

4 files changed

+60
-27
lines changed

mlir/include/mlir/Interfaces/SideEffectInterfaces.h

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -383,33 +383,71 @@ struct Write : public Effect::Base<Write> {};
383383
// SideEffect Utilities
384384
//===----------------------------------------------------------------------===//
385385

386-
/// Returns true if `op` has only an effect of type `EffectTy`.
386+
/// Return "true" if `op` has unknown effects. I.e., the effects of the
387+
/// operation itself are unknown and the operation does not derive its effects
388+
/// from its nested operations. (`HasRecursiveMemoryEffects` trait is not
389+
/// implemented or it is unknown whether it is implemented or not.)
390+
bool hasUnknownEffects(Operation *op);
391+
392+
/// Returns "true" if `op` has only an effect of type `EffectTy`. Returns
393+
/// "false" if `op` has unknown effects or other/additional effects. Recursive
394+
/// effects are not taken into account.
387395
template <typename EffectTy>
388396
bool hasSingleEffect(Operation *op);
389397

390-
/// Returns true if `op` has only an effect of type `EffectTy` (and of no other
391-
/// type) on `value`.
398+
/// Returns "true" if `op` has only an effect of type `EffectTy` on `value`.
399+
/// Returns "false" if `op` has unknown effects or other/additional effects.
400+
/// Recursive effects are not taken into account.
392401
template <typename EffectTy>
393402
bool hasSingleEffect(Operation *op, Value value);
394403

395-
/// Returns true if `op` has only an effect of type `EffectTy` (and of no other
396-
/// type) on `value` of type `ValueTy`.
404+
/// Returns "true" if `op` has only an effect of type `EffectTy` on `value` of
405+
/// type `ValueTy`. Returns "false" if `op` has unknown effects or
406+
/// other/additional effects. Recursive effects are not taken into account.
397407
template <typename ValueTy, typename EffectTy>
398408
bool hasSingleEffect(Operation *op, ValueTy value);
399409

400-
/// Returns true if `op` has an effect of type `EffectTy`.
410+
/// Returns "true" if `op` has an effect of type `EffectTy`. Returns "false" if
411+
/// `op` has unknown effects. Recursive effects are not taken into account.
401412
template <typename... EffectTys>
402413
bool hasEffect(Operation *op);
403414

404-
/// Returns true if `op` has an effect of type `EffectTy` on `value`.
415+
/// Returns "true" if `op` has an effect of type `EffectTy` on `value`. Returns
416+
/// "false" if `op` has unknown effects. Recursive effects are not taken into
417+
/// account.
405418
template <typename... EffectTys>
406419
bool hasEffect(Operation *op, Value value);
407420

408-
/// Returns true if `op` has an effect of type `EffectTy` on `value` of type
409-
/// `ValueTy`.
421+
/// Returns "true" if `op` has an effect of type `EffectTy` on `value` of type
422+
/// `ValueTy`. Returns "false" if `op` has unknown effects. Recursive effects
423+
/// are not taken into account.
410424
template <typename ValueTy, typename... EffectTys>
411425
bool hasEffect(Operation *op, ValueTy value);
412426

427+
/// Returns "true" if `op` might have an effect of type `EffectTy`. Returns
428+
/// "true" if the op has unknown effects. Recursive effects are not taken into
429+
/// account.
430+
template <typename... EffectTys>
431+
bool mightHaveEffect(Operation *op) {
432+
return hasUnknownEffects(op) || hasEffect<EffectTys...>(op);
433+
}
434+
435+
/// Returns "true" if `op` might have an effect of type `EffectTy` on `value`.
436+
/// Returns "true" if the op has unknown effects. Recursive effects are not
437+
/// taken into account.
438+
template <typename... EffectTys>
439+
bool mightHaveEffect(Operation *op, Value value) {
440+
return hasUnknownEffects(op) || hasEffect<EffectTys...>(op, value);
441+
}
442+
443+
/// Returns "true" if `op` might have an effect of type `EffectTy` on `value`
444+
/// of type `ValueTy`. Returns "true" if the op has unknown effects. Recursive
445+
/// effects are not taken into account.
446+
template <typename ValueTy, typename... EffectTys>
447+
bool mightHaveEffect(Operation *op, ValueTy value) {
448+
return hasUnknownEffects(op) || hasEffect<EffectTys...>(op, value);
449+
}
450+
413451
/// Return true if the given operation is unused, and has no side effects on
414452
/// memory that prevent erasing.
415453
bool isOpTriviallyDead(Operation *op);

mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,8 @@ static bool isMemref(Value v) { return isa<BaseMemRefType>(v.getType()); }
5151
/// Return "true" if the given op is guaranteed to have neither "Allocate" nor
5252
/// "Free" side effects.
5353
static bool hasNeitherAllocateNorFreeSideEffect(Operation *op) {
54-
if (isa<MemoryEffectOpInterface>(op))
55-
return !hasEffect<MemoryEffects::Allocate>(op) &&
56-
!hasEffect<MemoryEffects::Free>(op);
57-
// If the op does not implement the MemoryEffectOpInterface but has has
58-
// recursive memory effects, then this op in isolation (without its body) does
59-
// not have any side effects. All the ops inside the regions of this op will
60-
// be processed separately.
61-
return op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
54+
return !mightHaveEffect<MemoryEffects::Allocate>(op) &&
55+
!mightHaveEffect<MemoryEffects::Free>(op);
6256
}
6357

6458
/// Return "true" if the given op has buffer semantics. I.e., it has buffer
@@ -517,9 +511,7 @@ LogicalResult BufferDeallocation::verifyOperationPreconditions(Operation *op) {
517511
// MemoryEffectOpInterface. They usually do not have side effects apart
518512
// from the callee, which will be analyzed separately. (This is similar to
519513
// "recursive memory effects".)
520-
if (!isa<MemoryEffectOpInterface>(op) &&
521-
!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>() &&
522-
!isa<CallOpInterface>(op))
514+
if (hasUnknownEffects(op) && !isa<CallOpInterface>(op))
523515
return op->emitError(
524516
"ops with unknown memory side effects are not supported");
525517

mlir/lib/Interfaces/SideEffectInterfaces.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,11 @@ template bool
304304
mlir::hasEffect<BlockArgument, MemoryEffects::Write, MemoryEffects::Free>(
305305
Operation *, BlockArgument);
306306

307+
bool mlir::hasUnknownEffects(Operation *op) {
308+
return !isa<MemoryEffectOpInterface>(op) &&
309+
!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
310+
}
311+
307312
bool mlir::wouldOpBeTriviallyDead(Operation *op) {
308313
if (op->mightHaveTrait<OpTrait::IsTerminator>())
309314
return false;

mlir/lib/Transforms/CSE.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,10 @@ void CSEDriver::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
177177
bool CSEDriver::hasOtherSideEffectingOpInBetween(Operation *fromOp,
178178
Operation *toOp) {
179179
assert(fromOp->getBlock() == toOp->getBlock());
180-
assert(
181-
isa<MemoryEffectOpInterface>(fromOp) &&
182-
cast<MemoryEffectOpInterface>(fromOp).hasEffect<MemoryEffects::Read>() &&
183-
isa<MemoryEffectOpInterface>(toOp) &&
184-
cast<MemoryEffectOpInterface>(toOp).hasEffect<MemoryEffects::Read>());
180+
assert(hasEffect<MemoryEffects::Read>(fromOp) &&
181+
"expected read effect on fromOp");
182+
assert(hasEffect<MemoryEffects::Read>(toOp) &&
183+
"expected read effect on toOp");
185184
Operation *nextOp = fromOp->getNextNode();
186185
auto result =
187186
memEffectsCache.try_emplace(fromOp, std::make_pair(fromOp, nullptr));
@@ -245,11 +244,10 @@ LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
245244
// Some simple use case of operation with memory side-effect are dealt with
246245
// here. Operations with no side-effect are done after.
247246
if (!isMemoryEffectFree(op)) {
248-
auto memEffects = dyn_cast<MemoryEffectOpInterface>(op);
249247
// TODO: Only basic use case for operations with MemoryEffects::Read can be
250248
// eleminated now. More work needs to be done for more complicated patterns
251249
// and other side-effects.
252-
if (!memEffects || !memEffects.onlyHasEffect<MemoryEffects::Read>())
250+
if (!hasSingleEffect<MemoryEffects::Read>(op))
253251
return failure();
254252

255253
// Look for an existing definition for the operation.

0 commit comments

Comments
 (0)