[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.
This commit is contained in:
parent
2e2349e4e9
commit
0499d3a8cf
@ -383,33 +383,71 @@ struct Write : public Effect::Base<Write> {};
|
||||
// SideEffect Utilities
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Returns true if `op` has only an effect of type `EffectTy`.
|
||||
/// Return "true" if `op` has unknown effects. I.e., the effects of the
|
||||
/// operation itself are unknown and the operation does not derive its effects
|
||||
/// from its nested operations. (`HasRecursiveMemoryEffects` trait is not
|
||||
/// implemented or it is unknown whether it is implemented or not.)
|
||||
bool hasUnknownEffects(Operation *op);
|
||||
|
||||
/// Returns "true" if `op` has only an effect of type `EffectTy`. Returns
|
||||
/// "false" if `op` has unknown effects or other/additional effects. Recursive
|
||||
/// effects are not taken into account.
|
||||
template <typename EffectTy>
|
||||
bool hasSingleEffect(Operation *op);
|
||||
|
||||
/// Returns true if `op` has only an effect of type `EffectTy` (and of no other
|
||||
/// type) on `value`.
|
||||
/// Returns "true" if `op` has only an effect of type `EffectTy` on `value`.
|
||||
/// Returns "false" if `op` has unknown effects or other/additional effects.
|
||||
/// Recursive effects are not taken into account.
|
||||
template <typename EffectTy>
|
||||
bool hasSingleEffect(Operation *op, Value value);
|
||||
|
||||
/// Returns true if `op` has only an effect of type `EffectTy` (and of no other
|
||||
/// type) on `value` of type `ValueTy`.
|
||||
/// Returns "true" if `op` has only an effect of type `EffectTy` on `value` of
|
||||
/// type `ValueTy`. Returns "false" if `op` has unknown effects or
|
||||
/// other/additional effects. Recursive effects are not taken into account.
|
||||
template <typename ValueTy, typename EffectTy>
|
||||
bool hasSingleEffect(Operation *op, ValueTy value);
|
||||
|
||||
/// Returns true if `op` has an effect of type `EffectTy`.
|
||||
/// Returns "true" if `op` has an effect of type `EffectTy`. Returns "false" if
|
||||
/// `op` has unknown effects. Recursive effects are not taken into account.
|
||||
template <typename... EffectTys>
|
||||
bool hasEffect(Operation *op);
|
||||
|
||||
/// Returns true if `op` has an effect of type `EffectTy` on `value`.
|
||||
/// Returns "true" if `op` has an effect of type `EffectTy` on `value`. Returns
|
||||
/// "false" if `op` has unknown effects. Recursive effects are not taken into
|
||||
/// account.
|
||||
template <typename... EffectTys>
|
||||
bool hasEffect(Operation *op, Value value);
|
||||
|
||||
/// Returns true if `op` has an effect of type `EffectTy` on `value` of type
|
||||
/// `ValueTy`.
|
||||
/// Returns "true" if `op` has an effect of type `EffectTy` on `value` of type
|
||||
/// `ValueTy`. Returns "false" if `op` has unknown effects. Recursive effects
|
||||
/// are not taken into account.
|
||||
template <typename ValueTy, typename... EffectTys>
|
||||
bool hasEffect(Operation *op, ValueTy value);
|
||||
|
||||
/// Returns "true" if `op` might have an effect of type `EffectTy`. Returns
|
||||
/// "true" if the op has unknown effects. Recursive effects are not taken into
|
||||
/// account.
|
||||
template <typename... EffectTys>
|
||||
bool mightHaveEffect(Operation *op) {
|
||||
return hasUnknownEffects(op) || hasEffect<EffectTys...>(op);
|
||||
}
|
||||
|
||||
/// Returns "true" if `op` might have an effect of type `EffectTy` on `value`.
|
||||
/// Returns "true" if the op has unknown effects. Recursive effects are not
|
||||
/// taken into account.
|
||||
template <typename... EffectTys>
|
||||
bool mightHaveEffect(Operation *op, Value value) {
|
||||
return hasUnknownEffects(op) || hasEffect<EffectTys...>(op, value);
|
||||
}
|
||||
|
||||
/// Returns "true" if `op` might have an effect of type `EffectTy` on `value`
|
||||
/// of type `ValueTy`. Returns "true" if the op has unknown effects. Recursive
|
||||
/// effects are not taken into account.
|
||||
template <typename ValueTy, typename... EffectTys>
|
||||
bool mightHaveEffect(Operation *op, ValueTy value) {
|
||||
return hasUnknownEffects(op) || hasEffect<EffectTys...>(op, value);
|
||||
}
|
||||
|
||||
/// Return true if the given operation is unused, and has no side effects on
|
||||
/// memory that prevent erasing.
|
||||
bool isOpTriviallyDead(Operation *op);
|
||||
|
@ -51,14 +51,8 @@ static bool isMemref(Value v) { return isa<BaseMemRefType>(v.getType()); }
|
||||
/// Return "true" if the given op is guaranteed to have neither "Allocate" nor
|
||||
/// "Free" side effects.
|
||||
static bool hasNeitherAllocateNorFreeSideEffect(Operation *op) {
|
||||
if (isa<MemoryEffectOpInterface>(op))
|
||||
return !hasEffect<MemoryEffects::Allocate>(op) &&
|
||||
!hasEffect<MemoryEffects::Free>(op);
|
||||
// If the op does not implement the MemoryEffectOpInterface but has has
|
||||
// recursive memory effects, then this op in isolation (without its body) does
|
||||
// not have any side effects. All the ops inside the regions of this op will
|
||||
// be processed separately.
|
||||
return op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
|
||||
return !mightHaveEffect<MemoryEffects::Allocate>(op) &&
|
||||
!mightHaveEffect<MemoryEffects::Free>(op);
|
||||
}
|
||||
|
||||
/// Return "true" if the given op has buffer semantics. I.e., it has buffer
|
||||
@ -517,9 +511,7 @@ LogicalResult BufferDeallocation::verifyOperationPreconditions(Operation *op) {
|
||||
// MemoryEffectOpInterface. They usually do not have side effects apart
|
||||
// from the callee, which will be analyzed separately. (This is similar to
|
||||
// "recursive memory effects".)
|
||||
if (!isa<MemoryEffectOpInterface>(op) &&
|
||||
!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>() &&
|
||||
!isa<CallOpInterface>(op))
|
||||
if (hasUnknownEffects(op) && !isa<CallOpInterface>(op))
|
||||
return op->emitError(
|
||||
"ops with unknown memory side effects are not supported");
|
||||
|
||||
|
@ -304,6 +304,11 @@ template bool
|
||||
mlir::hasEffect<BlockArgument, MemoryEffects::Write, MemoryEffects::Free>(
|
||||
Operation *, BlockArgument);
|
||||
|
||||
bool mlir::hasUnknownEffects(Operation *op) {
|
||||
return !isa<MemoryEffectOpInterface>(op) &&
|
||||
!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
|
||||
}
|
||||
|
||||
bool mlir::wouldOpBeTriviallyDead(Operation *op) {
|
||||
if (op->mightHaveTrait<OpTrait::IsTerminator>())
|
||||
return false;
|
||||
|
@ -177,11 +177,10 @@ void CSEDriver::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
|
||||
bool CSEDriver::hasOtherSideEffectingOpInBetween(Operation *fromOp,
|
||||
Operation *toOp) {
|
||||
assert(fromOp->getBlock() == toOp->getBlock());
|
||||
assert(
|
||||
isa<MemoryEffectOpInterface>(fromOp) &&
|
||||
cast<MemoryEffectOpInterface>(fromOp).hasEffect<MemoryEffects::Read>() &&
|
||||
isa<MemoryEffectOpInterface>(toOp) &&
|
||||
cast<MemoryEffectOpInterface>(toOp).hasEffect<MemoryEffects::Read>());
|
||||
assert(hasEffect<MemoryEffects::Read>(fromOp) &&
|
||||
"expected read effect on fromOp");
|
||||
assert(hasEffect<MemoryEffects::Read>(toOp) &&
|
||||
"expected read effect on toOp");
|
||||
Operation *nextOp = fromOp->getNextNode();
|
||||
auto result =
|
||||
memEffectsCache.try_emplace(fromOp, std::make_pair(fromOp, nullptr));
|
||||
@ -245,11 +244,10 @@ LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
|
||||
// Some simple use case of operation with memory side-effect are dealt with
|
||||
// here. Operations with no side-effect are done after.
|
||||
if (!isMemoryEffectFree(op)) {
|
||||
auto memEffects = dyn_cast<MemoryEffectOpInterface>(op);
|
||||
// TODO: Only basic use case for operations with MemoryEffects::Read can be
|
||||
// eleminated now. More work needs to be done for more complicated patterns
|
||||
// and other side-effects.
|
||||
if (!memEffects || !memEffects.onlyHasEffect<MemoryEffects::Read>())
|
||||
if (!hasSingleEffect<MemoryEffects::Read>(op))
|
||||
return failure();
|
||||
|
||||
// Look for an existing definition for the operation.
|
||||
|
Loading…
x
Reference in New Issue
Block a user